introduction.knit (original) (raw)
This document gives an overview of the DNABarcodeCompatibility R package with a brief description of the set of tools that it contains. The package includes six main functions that are briefly described below with examples. These functions allow one to load a list of DNA barcodes (such as the Illumina TruSeq small RNA kits), to filter these barcodes according to distance and nucleotide content criteria, to generate sets of compatible barcode combinations out of the filtered barcode list, and finally to generate an optimized selection of barcode combinations for multiplex sequencing experiments. In particular, the package provides an optimizer function to favour the selection of compatible barcode combinations withleast heterogeneity in the frequencies of DNA barcodes, and allows one to keep barcodes that are robust against substitution and insertion/deletion errors, thereby facilitating the demultiplexing step.
The package deals with the three existing sequencing-by-synthesis chemistries from Illumina:
Load the package
library("DNABarcodeCompatibility")
Define a helper function to save the raw dataset as a temporary text file
# This function is created for the purpose of the documentation
export_dataset_to_file =
function(dataset = DNABarcodeCompatibility::IlluminaIndexesRaw) {
if ("data.frame" %in% is(dataset)) {
write.table(dataset,
textfile <- tempfile(),
row.names = FALSE, col.names = FALSE, quote=FALSE)
return(textfile)
} else print(paste("The input dataset isn't a data.frame:",
"NOT exported into file"))
}
Design an experiment
The function experiment_design()
uses a Shannon-entropy maximization approach to identify a set of compatible barcode combinations in which the frequencies of occurrences of the various DNA barcodes are as uniform as possible. The optimization can be performed in the contexts of single and dual barcoding. It performs either an exhaustive or a random search of compatible DNA-barcode combinations, depending on the size of the DNA-barcode set used, and on the number of samples to be multiplexed.
Examples for single indexing
- 12 libraries sequenced in multiplex of 3 on a HiSeq (4 channels) platform
txtfile <- export_dataset_to_file (
dataset = DNABarcodeCompatibility::IlluminaIndexesRaw
)
experiment_design(file1=txtfile,
sample_number=12,
mplex_level=3,
platform=4)
## [1] "Theoretical max entropy: 2.48491"
## [1] "Entropy of the optimized set: 2.48491"
## sample Lane Id sequence
## 1 1 1 RPI25 ACTGAT
## 2 2 1 RPI40 CTCAGA
## 3 3 1 RPI43 TACAGC
## 4 4 2 RPI03 TTAGGC
## 5 5 2 RPI09 GATCAG
## 6 6 2 RPI29 CAACTA
## 7 7 3 RPI20 GTGGCC
## 8 8 3 RPI23 GAGTGG
## 9 9 3 RPI28 CAAAAG
## 10 10 4 RPI10 TAGCTT
## 11 11 4 RPI27 ATTCCT
## 12 12 4 RPI41 GACGAC
- 12 libraries sequenced in multiplex of 3 on a NextSeq (2 channels) platform
txtfile <- export_dataset_to_file (
dataset = DNABarcodeCompatibility::IlluminaIndexesRaw
)
experiment_design(file1=txtfile,
sample_number=12,
mplex_level=3,
platform=2)
## [1] "Theoretical max entropy: 2.48491"
## [1] "Entropy of the optimized set: 2.48491"
## sample Lane Id sequence
## 1 1 1 RPI29 CAACTA
## 2 2 1 RPI33 CAGGCG
## 3 3 1 RPI45 TCATTC
## 4 4 2 RPI01 ATCACG
## 5 5 2 RPI14 AGTTCC
## 6 6 2 RPI31 CACGAT
## 7 7 3 RPI04 TGACCA
## 8 8 3 RPI28 CAAAAG
## 9 9 3 RPI40 CTCAGA
## 10 10 4 RPI10 TAGCTT
## 11 11 4 RPI19 GTGAAA
## 12 12 4 RPI36 CCAACA
- 12 libraries sequenced in multiplex of 3 on a iSeq (1 channels) platform
txtfile <- export_dataset_to_file (
dataset = DNABarcodeCompatibility::IlluminaIndexesRaw
)
experiment_design(file1=txtfile,
sample_number=12,
mplex_level=3,
platform=1)
## [1] "Theoretical max entropy: 2.48491"
## [1] "Entropy of the optimized set: 2.48491"
## sample Lane Id sequence
## 1 1 1 RPI02 CGATGT
## 2 2 1 RPI27 ATTCCT
## 3 3 1 RPI44 TATAAT
## 4 4 2 RPI07 CAGATC
## 5 5 2 RPI10 TAGCTT
## 6 6 2 RPI25 ACTGAT
## 7 7 3 RPI11 GGCTAC
## 8 8 3 RPI26 ATGAGC
## 9 9 3 RPI36 CCAACA
## 10 10 4 RPI04 TGACCA
## 11 11 4 RPI15 ATGTCA
## 12 12 4 RPI35 CATTTT
- 12 libraries sequenced in multiplex of 3 on a HiSeq platform using barcodes robust against 1 substitution error
txtfile <- export_dataset_to_file (
dataset = DNABarcodeCompatibility::IlluminaIndexesRaw
)
experiment_design(file1=txtfile,
sample_number=12,
mplex_level=3,
platform=4,
metric = "hamming",
d = 3)
## [1] "Theoretical max entropy: 2.48491"
## [1] "Entropy of the optimized set: 2.48491"
## sample Lane Id sequence
## 1 1 1 RPI03 TTAGGC
## 2 2 1 RPI27 ATTCCT
## 3 3 1 RPI44 TATAAT
## 4 4 2 RPI16 CCGTCC
## 5 5 2 RPI24 GGTAGC
## 6 6 2 RPI30 CACCGG
## 7 7 3 RPI10 TAGCTT
## 8 8 3 RPI20 GTGGCC
## 9 9 3 RPI31 CACGAT
## 10 10 4 RPI01 ATCACG
## 11 11 4 RPI05 ACAGTG
## 12 12 4 RPI19 GTGAAA
Examples for dual indexing
- 12 libraries sequenced in multiplex of 3 on a HiSeq platform
# Select the first half of barcodes from the dataset
txtfile1 <- export_dataset_to_file (
DNABarcodeCompatibility::IlluminaIndexesRaw[1:24,]
)
# Select the second half of barcodes from the dataset
txtfile2 <- export_dataset_to_file (
DNABarcodeCompatibility::IlluminaIndexesRaw[25:48,]
)
# Get compatibles combinations of least redundant barcodes
experiment_design(file1=txtfile1,
sample_number=12,
mplex_level=3,
platform=4,
file2=txtfile2)
## [1] "Theoretical max entropy: 2.48491"
## [1] "Entropy of the optimized set: 2.48491"
## Id Lane
## 1 RPI07 1
## 2 RPI14 1
## 3 RPI17 1
## 4 RPI10 2
## 5 RPI11 2
## 6 RPI13 2
## 7 RPI04 3
## 8 RPI06 3
## 9 RPI08 3
## 10 RPI01 4
## 11 RPI03 4
## 12 RPI23 4
## [1] "Theoretical max entropy: 2.48491"
## [1] "Entropy of the optimized set: 2.48491"
## Id Lane
## 1 RPI27 1
## 2 RPI45 1
## 3 RPI48 1
## 4 RPI35 2
## 5 RPI37 2
## 6 RPI46 2
## 7 RPI25 3
## 8 RPI26 3
## 9 RPI42 3
## 10 RPI29 4
## 11 RPI39 4
## 12 RPI44 4
## Id Lane sequence
## 1 RPI07 1 CAGATC
## 2 RPI14 1 AGTTCC
## 3 RPI17 1 GTAGAG
## 4 RPI10 2 TAGCTT
## 5 RPI11 2 GGCTAC
## 6 RPI13 2 AGTCAA
## 7 RPI04 3 TGACCA
## 8 RPI06 3 GCCAAT
## 9 RPI08 3 ACTTGA
## 10 RPI01 4 ATCACG
## 11 RPI03 4 TTAGGC
## 12 RPI23 4 GAGTGG
## Id Lane sequence
## 1 RPI27 1 ATTCCT
## 2 RPI45 1 TCATTC
## 3 RPI48 1 TCGGCA
## 4 RPI35 2 CATTTT
## 5 RPI37 2 CGGAAT
## 6 RPI46 2 TCCCGA
## 7 RPI25 3 ACTGAT
## 8 RPI26 3 ATGAGC
## 9 RPI42 3 TAATCG
## 10 RPI29 4 CAACTA
## 11 RPI39 4 CTATAC
## 12 RPI44 4 TATAAT
## sample Lane Id1 sequence1 Id2 sequence2
## 1 1 1 RPI07 CAGATC RPI27 ATTCCT
## 2 2 1 RPI14 AGTTCC RPI45 TCATTC
## 3 3 1 RPI17 GTAGAG RPI48 TCGGCA
## 4 4 2 RPI10 TAGCTT RPI35 CATTTT
## 5 5 2 RPI11 GGCTAC RPI37 CGGAAT
## 6 6 2 RPI13 AGTCAA RPI46 TCCCGA
## 7 7 3 RPI04 TGACCA RPI25 ACTGAT
## 8 8 3 RPI06 GCCAAT RPI26 ATGAGC
## 9 9 3 RPI08 ACTTGA RPI42 TAATCG
## 10 10 4 RPI01 ATCACG RPI29 CAACTA
## 11 11 4 RPI03 TTAGGC RPI39 CTATAC
## 12 12 4 RPI23 GAGTGG RPI44 TATAAT
- 12 libraries sequenced in multiplex of 3 on a HiSeq platform using barcodes robust against 1 substitution error
# Select the first half of barcodes from the dataset
txtfile1 <- export_dataset_to_file (
DNABarcodeCompatibility::IlluminaIndexesRaw[1:24,]
)
# Select the second half of barcodes from the dataset
txtfile2 <- export_dataset_to_file (
DNABarcodeCompatibility::IlluminaIndexesRaw[25:48,]
)
# Get compatibles combinations of least redundant barcodes
experiment_design(file1=txtfile1, sample_number=12, mplex_level=3, platform=4,
file2=txtfile2, metric="hamming", d=3)
## [1] "Theoretical max entropy: 2.48491"
## [1] "Entropy of the optimized set: 2.48491"
## Id Lane
## 1 RPI04 1
## 2 RPI13 1
## 3 RPI23 1
## 4 RPI01 2
## 5 RPI08 2
## 6 RPI24 2
## 7 RPI05 3
## 8 RPI10 3
## 9 RPI20 3
## 10 RPI06 4
## 11 RPI07 4
## 12 RPI14 4
## [1] "Theoretical max entropy: 2.48491"
## [1] "Entropy of the optimized set: 2.48491"
## Id Lane
## 1 RPI25 1
## 2 RPI40 1
## 3 RPI47 1
## 4 RPI26 2
## 5 RPI39 2
## 6 RPI42 2
## 7 RPI35 3
## 8 RPI37 3
## 9 RPI41 3
## 10 RPI27 4
## 11 RPI34 4
## 12 RPI46 4
## Id Lane sequence
## 1 RPI04 1 TGACCA
## 2 RPI13 1 AGTCAA
## 3 RPI23 1 GAGTGG
## 4 RPI01 2 ATCACG
## 5 RPI08 2 ACTTGA
## 6 RPI24 2 GGTAGC
## 7 RPI05 3 ACAGTG
## 8 RPI10 3 TAGCTT
## 9 RPI20 3 GTGGCC
## 10 RPI06 4 GCCAAT
## 11 RPI07 4 CAGATC
## 12 RPI14 4 AGTTCC
## Id Lane sequence
## 1 RPI25 1 ACTGAT
## 2 RPI40 1 CTCAGA
## 3 RPI47 1 TCGAAG
## 4 RPI26 2 ATGAGC
## 5 RPI39 2 CTATAC
## 6 RPI42 2 TAATCG
## 7 RPI35 3 CATTTT
## 8 RPI37 3 CGGAAT
## 9 RPI41 3 GACGAC
## 10 RPI27 4 ATTCCT
## 11 RPI34 4 CATGGC
## 12 RPI46 4 TCCCGA
## sample Lane Id1 sequence1 Id2 sequence2
## 1 1 1 RPI04 TGACCA RPI25 ACTGAT
## 2 2 1 RPI13 AGTCAA RPI40 CTCAGA
## 3 3 1 RPI23 GAGTGG RPI47 TCGAAG
## 4 4 2 RPI01 ATCACG RPI26 ATGAGC
## 5 5 2 RPI08 ACTTGA RPI39 CTATAC
## 6 6 2 RPI24 GGTAGC RPI42 TAATCG
## 7 7 3 RPI05 ACAGTG RPI35 CATTTT
## 8 8 3 RPI10 TAGCTT RPI37 CGGAAT
## 9 9 3 RPI20 GTGGCC RPI41 GACGAC
## 10 10 4 RPI06 GCCAAT RPI27 ATTCCT
## 11 11 4 RPI07 CAGATC RPI34 CATGGC
## 12 12 4 RPI14 AGTTCC RPI46 TCCCGA
Build your own workflow
This section guides you through the detailed API of the package with the aim to help you build your own workflow. The package is designed to be flexible and should be easily adaptable to most experimental contexts, using theexperiment_design()
function as a template, or building your own workflow from scratch.
Load and check a dataset of barcodes
The file_loading_and_checking()
function loads the file containing the DNA barcodes set and analyzes its content. In particular, it checks that each barcode in the set is unique and uniquely identified (removing any repetition that occurs). It also checks the homogeneity of size of the barcodes, calculates their GC content and detects the presence of homopolymers of length >= 3.
file_loading_and_checking(
file = export_dataset_to_file(
dataset = DNABarcodeCompatibility::IlluminaIndexesRaw
)
)
## Id sequence GC_content homopolymer
## 1 RPI01 ATCACG 50.00 FALSE
## 2 RPI02 CGATGT 50.00 FALSE
## 3 RPI03 TTAGGC 50.00 FALSE
## 4 RPI04 TGACCA 50.00 FALSE
## 5 RPI05 ACAGTG 50.00 FALSE
## 6 RPI06 GCCAAT 50.00 FALSE
## 7 RPI07 CAGATC 50.00 FALSE
## 8 RPI08 ACTTGA 33.33 FALSE
## 9 RPI09 GATCAG 50.00 FALSE
## 10 RPI10 TAGCTT 33.33 FALSE
## 11 RPI11 GGCTAC 66.67 FALSE
## 12 RPI12 CTTGTA 33.33 FALSE
## 13 RPI13 AGTCAA 33.33 FALSE
## 14 RPI14 AGTTCC 50.00 FALSE
## 15 RPI15 ATGTCA 33.33 FALSE
## 16 RPI16 CCGTCC 83.33 FALSE
## 17 RPI17 GTAGAG 50.00 FALSE
## 18 RPI18 GTCCGC 83.33 FALSE
## 19 RPI19 GTGAAA 33.33 TRUE
## 20 RPI20 GTGGCC 83.33 FALSE
## 21 RPI21 GTTTCG 50.00 TRUE
## 22 RPI22 CGTACG 66.67 FALSE
## 23 RPI23 GAGTGG 66.67 FALSE
## 24 RPI24 GGTAGC 66.67 FALSE
## 25 RPI25 ACTGAT 33.33 FALSE
## 26 RPI26 ATGAGC 50.00 FALSE
## 27 RPI27 ATTCCT 33.33 FALSE
## 28 RPI28 CAAAAG 33.33 TRUE
## 29 RPI29 CAACTA 33.33 FALSE
## 30 RPI30 CACCGG 83.33 FALSE
## 31 RPI31 CACGAT 50.00 FALSE
## 32 RPI32 CACTCA 50.00 FALSE
## 33 RPI33 CAGGCG 83.33 FALSE
## 34 RPI34 CATGGC 66.67 FALSE
## 35 RPI35 CATTTT 16.67 TRUE
## 36 RPI36 CCAACA 50.00 FALSE
## 37 RPI37 CGGAAT 50.00 FALSE
## 38 RPI38 CTAGCT 50.00 FALSE
## 39 RPI39 CTATAC 33.33 FALSE
## 40 RPI40 CTCAGA 50.00 FALSE
## 41 RPI41 GACGAC 66.67 FALSE
## 42 RPI42 TAATCG 33.33 FALSE
## 43 RPI43 TACAGC 50.00 FALSE
## 44 RPI44 TATAAT 0.00 FALSE
## 45 RPI45 TCATTC 33.33 FALSE
## 46 RPI46 TCCCGA 66.67 TRUE
## 47 RPI47 TCGAAG 50.00 FALSE
## 48 RPI48 TCGGCA 66.67 FALSE
Examples of an exhaustive search of compatible barcode combinations
The total number of combinations depends on the number of available barcodes and of the multiplex level. For 48 barcodes and a multiplex level of 3, the total number of combinations (compatible or not) can be calculated usingchoose(48,3)
, which gives 17296 combinations. In many cases the total number of combinations can become much larger (even gigantic), and one cannot perform an exhaustive search (see get_random_combinations()
below).
- 48 barcodes, multiplex level of 2, HiSeq platform
# Total number of combinations
choose(48,2)
## [1] 1128
# Load barcodes
barcodes <- DNABarcodeCompatibility::IlluminaIndexes
# Time for an exhaustive search
system.time(m <- get_all_combinations(index_df = barcodes,
mplex_level = 2,
platform = 4))
## user system elapsed
## 0.325 0.000 0.326
# Each line represents a compatible combination of barcodes
head(m)
## [,1] [,2]
## [1,] "RPI04" "RPI35"
## [2,] "RPI05" "RPI19"
## [3,] "RPI06" "RPI12"
## [4,] "RPI07" "RPI17"
## [5,] "RPI10" "RPI39"
## [6,] "RPI18" "RPI25"
- 48 barcodes, multiplex level of 3, HiSeq platform
# Total number of combinations
choose(48,3)
## [1] 17296
# Load barcodes
barcodes <- DNABarcodeCompatibility::IlluminaIndexes
# Time for an exhaustive search
system.time(m <- get_all_combinations(index_df = barcodes,
mplex_level = 3,
platform = 4))
## user system elapsed
## 6.654 0.037 6.691
# Each line represents a compatible combination of barcodes
head(m)
## [,1] [,2] [,3]
## [1,] "RPI01" "RPI02" "RPI48"
## [2,] "RPI01" "RPI03" "RPI07"
## [3,] "RPI01" "RPI03" "RPI08"
## [4,] "RPI01" "RPI03" "RPI09"
## [5,] "RPI01" "RPI03" "RPI10"
## [6,] "RPI01" "RPI03" "RPI16"
Examples of a random search of compatible barcode combinations
When the total number of combinations is too high, it is recommended to pick combinations at random and then select those that are compatible.
- 48 barcodes, multiplex level of 3, HiSeq platform
# Total number of combinations
choose(48,3)
## [1] 17296
# Load barcodes
barcodes <- DNABarcodeCompatibility::IlluminaIndexes
# Time for a random search
system.time(m <- get_random_combinations(index_df = barcodes,
mplex_level = 2,
platform = 4))
## user system elapsed
## 0.223 0.005 0.228
# Each line represents a compatible combination of barcodes
head(m)
## [,1] [,2]
## [1,] "RPI07" "RPI17"
## [2,] "RPI10" "RPI39"
## [3,] "RPI18" "RPI33"
## [4,] "RPI21" "RPI29"
## [5,] "RPI22" "RPI45"
## [6,] "RPI24" "RPI31"
- 48 barcodes, multiplex level of 4, HiSeq platform
# Total number of combinations
choose(48,4)
## [1] 194580
# Load barcodes
barcodes <- DNABarcodeCompatibility::IlluminaIndexes
# Time for a random search
system.time(m <- get_random_combinations(index_df = barcodes,
mplex_level = 4,
platform = 4))
## user system elapsed
## 1.165 0.000 1.165
# Each line represents a compatible combination of barcodes
head(m)
## [,1] [,2] [,3] [,4]
## [1,] "RPI01" "RPI17" "RPI24" "RPI46"
## [2,] "RPI01" "RPI10" "RPI33" "RPI40"
## [3,] "RPI01" "RPI07" "RPI11" "RPI45"
## [4,] "RPI01" "RPI20" "RPI28" "RPI34"
## [5,] "RPI01" "RPI10" "RPI11" "RPI25"
## [6,] "RPI01" "RPI12" "RPI17" "RPI30"
- 48 barcodes, multiplex level of 6, HiSeq platform
# Total number of combinations
choose(48,6)
## [1] 12271512
# Load barcodes
barcodes <- DNABarcodeCompatibility::IlluminaIndexes
# Time for a random search
system.time(m <- get_random_combinations(index_df = barcodes,
mplex_level = 6,
platform = 4))
## user system elapsed
## 1.975 0.001 1.986
# Each line represents a compatible combination of barcodes
head(m)
## [,1] [,2] [,3] [,4] [,5] [,6]
## [1,] "RPI01" "RPI09" "RPI11" "RPI22" "RPI23" "RPI46"
## [2,] "RPI01" "RPI20" "RPI21" "RPI22" "RPI29" "RPI41"
## [3,] "RPI01" "RPI06" "RPI08" "RPI24" "RPI33" "RPI35"
## [4,] "RPI01" "RPI06" "RPI14" "RPI29" "RPI34" "RPI40"
## [5,] "RPI01" "RPI03" "RPI05" "RPI23" "RPI30" "RPI44"
## [6,] "RPI01" "RPI06" "RPI11" "RPI28" "RPI33" "RPI45"
Constrain barcodes to be robust against one substitution error
# Load barcodes
barcodes <- DNABarcodeCompatibility::IlluminaIndexes
# Perform a random search of compatible combinations
m <- get_random_combinations(index_df = barcodes,
mplex_level = 3,
platform = 4)
# Keep barcodes that are robust against one substitution error
filtered_m <- distance_filter(index_df = barcodes,
combinations_m = m,
metric = "hamming",
d = 3)
# Each line represents a compatible combination of barcodes
head(filtered_m)
## V1 V2 V3
## [1,] "RPI01" "RPI08" "RPI48"
## [2,] "RPI01" "RPI16" "RPI46"
## [3,] "RPI01" "RPI12" "RPI44"
## [4,] "RPI01" "RPI35" "RPI48"
## [5,] "RPI01" "RPI08" "RPI21"
## [6,] "RPI01" "RPI08" "RPI46"
Optimize the set of compatible combinations to reduce barcode redundancy
# Keep set of compatible barcodes that are robust against one substitution
# error
filtered_m <- distance_filter(
index_df = DNABarcodeCompatibility::IlluminaIndexes,
combinations_m = get_random_combinations(index_df = barcodes,
mplex_level = 3,
platform = 4),
metric = "hamming", d = 3)
# Use a Shannon-entropy maximization approach to reduce barcode redundancy
df <- optimize_combinations(combination_m = filtered_m,
nb_lane = 12,
index_number = 48)
## [1] "Theoretical max entropy: 3.58352"
## [1] "Entropy of the optimized set: 3.58352"
# Each line represents a compatible combination of barcodes and each row a lane
# of the flow cell
df
## V1 V2 V3
## [1,] "RPI13" "RPI22" "RPI45"
## [2,] "RPI03" "RPI30" "RPI44"
## [3,] "RPI17" "RPI27" "RPI29"
## [4,] "RPI05" "RPI39" "RPI47"
## [5,] "RPI37" "RPI41" "RPI46"
## [6,] "RPI16" "RPI24" "RPI38"
## [7,] "RPI20" "RPI31" "RPI43"
## [8,] "RPI06" "RPI25" "RPI26"
## [9,] "RPI04" "RPI28" "RPI35"
## [10,] "RPI18" "RPI33" "RPI36"
## [11,] "RPI01" "RPI07" "RPI11"
## [12,] "RPI10" "RPI14" "RPI42"
The optimized result isn’t an optimum when filtering out too many barcodes
- Increased distance between barcode sequences: redundancy may become inevitable
# Keep set of compatible barcodes that are robust against multiple substitution
# and insertion/deletion errors
filtered_m <- distance_filter(
index_df = DNABarcodeCompatibility::IlluminaIndexes,
combinations_m = get_random_combinations(index_df = barcodes,
mplex_level = 3,
platform = 4),
metric = "seqlev", d = 4)
# Use a Shannon-entropy maximization approach to reduce barcode redundancy
df <- optimize_combinations(combination_m = filtered_m,
nb_lane = 12,
index_number = 48)
## [1] "Theoretical max entropy: 3.58352"
## [1] "Entropy of the optimized set: 2.57211"
# Each line represents a compatible combination of barcodes and each row a
# lane of the flow cell
df
## V1 V2 V3
## [1,] "RPI16" "RPI24" "RPI28"
## [2,] "RPI03" "RPI37" "RPI41"
## [3,] "RPI01" "RPI19" "RPI35"
## [4,] "RPI03" "RPI37" "RPI41"
## [5,] "RPI01" "RPI19" "RPI35"
## [6,] "RPI16" "RPI24" "RPI28"
## [7,] "RPI24" "RPI35" "RPI36"
## [8,] "RPI18" "RPI33" "RPI35"
## [9,] "RPI21" "RPI34" "RPI36"
## [10,] "RPI21" "RPI34" "RPI36"
## [11,] "RPI21" "RPI34" "RPI36"
## [12,] "RPI03" "RPI37" "RPI41"