OSCR

Sibling chimerism among microglia in marmosets.

Code ↔ Paper

6 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 6 matches
  1. [1] § Methods › Donor-of-origin analysis and detection of host-sibling doublets (Dropulation) ↔ src/java/org/broadinstitute/dropseqrna/barnyard/digitalallelecounts/sampleassignment/multisample/DetectDoublets.java, lines 64–123 · score 0.69 · DetectDoublets, donor likelihood, AssignCellsToSamples, cell barcodes, Dropulation, threshold
  2. [2] § Methods › Latent factor analysis ↔ R/preprocessing.R, lines 3793–3878 · score 0.68 · Pearson residuals, variable features, glm, latent, SCT, regression
  3. [3] § Methods › Clustering of cells using independent component analysis ↔ R/integration.R, lines 3267–3410 · score 0.58 · nearest neighbor, expression matrix, algorithm, variable, libraries, cells
  4. [4] § Methods › Binomial generalized linear mixed-effects model analysis ↔ R/differential_expression.R, lines 455–538 · score 0.56 · binomial generalized linear, predictor, vector, identity, model, fraction
  5. [5] § Methods › Latent factor analysis ↔ R/vst.R, lines 5–109 · score 0.53 · Pearson residuals, glm, latent, regression, transform, variable
  6. [6] § Methods › Gene expression analysis of host and sibling meta cells ↔ src/python/src/dropseq/eqtl/normalize_tensorqtl_expression.py, lines 80–143 · score 0.52 · edgeR, fold change, sum, gene

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Java · 704 lines · 38 KB · MIT · 1 match

  1. /*
  2. * MIT License
  3. *
  4. * Copyright 2017 Broad Institute
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in all
  14. * copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  22. * SOFTWARE.
  23. */
  24. package org.broadinstitute.dropseqrna.barnyard.digitalallelecounts.sampleassignment.multisample;
  25. import java.io.File;
  26. import java.io.PrintStream;
  27. import java.text.DecimalFormat;
  28. import java.util.ArrayList;
  29. import java.util.Arrays;
  30. import java.util.Collections;
  31. import java.util.Comparator;
  32. import java.util.HashMap;
  33. import java.util.HashSet;
  34. import java.util.List;
  35. import java.util.Map;
  36. import java.util.Set;
  37. import org.apache.commons.lang3.StringUtils;
  38. import org.broadinstitute.barclay.argparser.Argument;
  39. import org.broadinstitute.barclay.argparser.CommandLineProgramProperties;
  40. import org.broadinstitute.dropseqrna.barnyard.GeneFunctionCommandLineBase;
  41. import org.broadinstitute.dropseqrna.barnyard.ParseBarcodeFile;
  42. import org.broadinstitute.dropseqrna.barnyard.digitalallelecounts.SNPUMIBasePileupIterator;
  43. import org.broadinstitute.dropseqrna.barnyard.digitalallelecounts.SortOrder;
  44. import org.broadinstitute.dropseqrna.barnyard.digitalallelecounts.sampleassignment.CellAssignmentUtils;
  45. import org.broadinstitute.dropseqrna.barnyard.digitalallelecounts.sampleassignment.CellCollectionSampleLikelihoodCollection;
  46. import org.broadinstitute.dropseqrna.barnyard.digitalallelecounts.sampleassignment.SampleGenotypeProbabilities;
  47. import org.broadinstitute.dropseqrna.barnyard.digitalallelecounts.sampleassignment.SampleGenotypeProbabilitiesIterator;
  48. import org.broadinstitute.dropseqrna.cmdline.CustomCommandLineValidationHelper;
  49. import org.broadinstitute.dropseqrna.cmdline.DropSeq;
  50. import org.broadinstitute.dropseqrna.utils.AssertSequenceDictionaryIntersection;
  51. import org.broadinstitute.dropseqrna.utils.FileListParsingUtils;
  52. import org.broadinstitute.dropseqrna.utils.FileUtils;
  53. import org.broadinstitute.dropseqrna.utils.GroupingIterator;
  54. import org.broadinstitute.dropseqrna.utils.VCFUtils;
  55. import org.broadinstitute.dropseqrna.utils.io.ErrorCheckingPrintStream;
  56. import org.broadinstitute.dropseqrna.utils.readiterators.IgnoreGeneAnnotationTagger;
  57. import org.broadinstitute.dropseqrna.utils.readiterators.PCRDuplicateFilteringIterator;
  58. import org.broadinstitute.dropseqrna.utils.readiterators.SamFileMergeUtil;
  59. import org.broadinstitute.dropseqrna.utils.readiterators.SamHeaderAndIterator;
  60. import org.broadinstitute.dropseqrna.vcftools.SampleAssignmentVCFUtils;
  61. import htsjdk.samtools.SAMRecord;
  62. import htsjdk.samtools.SAMSequenceDictionary;
  63. import htsjdk.samtools.SamReaderFactory;
  64. import htsjdk.samtools.util.CloseableIterator;
  65. import htsjdk.samtools.util.IOUtil;
  66. import htsjdk.samtools.util.Interval;
  67. import htsjdk.samtools.util.IntervalList;
  68. import htsjdk.samtools.util.Log;
  69. import htsjdk.samtools.util.PeekableIterator;
  70. import htsjdk.variant.variantcontext.VariantContext;
  71. import htsjdk.variant.vcf.VCFFileReader;
  72. import picard.cmdline.StandardOptionDefinitions;
  73. import picard.nio.PicardHtsPath;
  74. @CommandLineProgramProperties(summary = "Detect Doublets in Dropulation Data. Uses the outputs of AssignCellsToSamples to make decisions. It's highly recommended to use the VCF output from AssignCellsToSamples as input, as"
  75. + "the memory usage of a full VCF may be prohibitive compared to the AssignCellsToSamples VCF, which contains only the variants that were observed in the data. This also greatly speeds up"
  76. + "analysis.", oneLineSummary = "Detect Doublets in Dropulation Data", programGroup = DropSeq.class)
  77. public class DetectDoublets extends GeneFunctionCommandLineBase {
  78. private static final Log log = Log.getInstance(DetectDoublets.class);
  79. @Argument(shortName = StandardOptionDefinitions.INPUT_SHORT_NAME, doc = "The input SAM or BAM file to analyze. This argument can accept wildcards, or a file with the suffix .bam_list that contains the locations of multiple BAM files", minElements = 1)
  80. public List<PicardHtsPath> INPUT_BAM;
  81. @Argument(doc = "The input VCF file to analyze. Use the output VCF from AssignCellsToSamples to save memory.")
  82. public PicardHtsPath VCF;
  83. @Argument(doc = "The output likelihood file from AssignCellsToSamples")
  84. public File SINGLE_DONOR_LIKELIHOOD_FILE;
  85. @Argument(shortName = StandardOptionDefinitions.OUTPUT_SHORT_NAME, doc = "Output file of doublet likelihoods. This supports zipped formats like gz and bz2.")
  86. public File OUTPUT;
  87. @Argument(doc = "Output file of per-sample-pair doublet likelihoods. Insted of just the best pair as seen in the OUTPUT file, "
  88. + "this outputs all tested pairs for each cell. This supports zipped formats like gz and bz2.", optional = true)
  89. public File OUTPUT_ALL_PAIRS = null;
  90. @Argument(doc = "Output file of per-snp/sample-pair doublet likelihoods. Insted of just the best pair as seen in the OUTPUT file, "
  91. + "this outputs all tested pairs for each cell, for each SNP. This file can get pretty obnoxiously huge."
  92. + "This supports zipped formats like gz and bz2.", optional = true)
  93. public File OUTPUT_PER_SNP = null;
  94. @Argument(doc = "The cell barcode tag. If there are no reads with this tag, the program will assume that all reads belong to the same cell and process in single sample mode.")
  95. public String CELL_BARCODE_TAG = "XC";
  96. @Argument(doc = "The molecular barcode tag.")
  97. public String MOLECULAR_BARCODE_TAG = "XM";
  98. @Argument(doc = "The edit distance that molecular barcodes should be combined at within a gene/SNP.")
  99. public Integer EDIT_DISTANCE = 1;
  100. @Argument(doc = "The map quality of the read to be included.")
  101. public Integer READ_MQ = 10;
  102. @Argument(doc = "Override NUM_CORE_BARCODES and process reads that have the cell barcodes in this file instead. The file has 1 column with no header.", optional = false)
  103. public File CELL_BC_FILE = null;
  104. @Argument(doc = "The minimum genotype quality for a variant. Set this value to 0 to not filter by GQ scores if they are present, or to -1 to completely "
  105. + "ignore GQ values if they are not set in the genotype info field. If the GQ field is not set in the VCF header, this will be set to -1 by default.")
  106. public Integer GQ_THRESHOLD = 30;
  107. @Argument(doc = "A file with a list of samples in the VCF to consider as samples in the doublets. This subsets the VCF into a smaller data set containing only the samples listed. "
  108. + "The file has 1 column with no header. If this list contains only one donor and no contaminating donors were found via single donor assignment, "
  109. + "doublet detection calculations will not take place. Instead, the program will emit a default output for each cell, and the program "
  110. + "will then quit with an exit status.", optional = false)
  111. public File SAMPLE_FILE;
  112. @Argument(doc = "Instead of useing base qualities to determine error rate, use a fixed error rate instead. This is rounded to the nearest phread score internally.", optional = true)
  113. public Double FIXED_ERROR_RATE = null;
  114. @Argument(doc = "Caps the base error rate at a maximum probability so no SNP can be weighed more than this value. For example, if this value was 0.01, "
  115. + "then a base quality 30 value (normally an erro rate of 0.001) would become 0.01. With the same threshold, a base with an error rate of 0.1 would be unaffected.", optional = true)
  116. public Double MAX_ERROR_RATE = null;
  117. @Argument(doc = "A file that contains an estimate of how much ambient RNA is in each cell. This is a fractional estimate between 0 and 1. File is tab seperated, with 2 columns:"
  118. + "cell_barcode and frac_contamination. When supplied along side the ALLELE_FREQUENCY_ESTIMATE_FILE, this modifies the likelihood error rates to take into account how often"
  119. + "the allele observed can be drawn from ambient RNA. We use cellbender remove background [https://github.com/broadinstitute/CellBender] to estimate the "
  120. + "number of transcripts before and after ambient cleanup to define the fraction of transcripts that come from ambient RNA.", optional = true)
  121. public File CELL_CONTAMINATION_ESTIMATE_FILE = null;
  122. @Argument(doc = "A file that contains an estimate of the allele frequency expected for each SNP across donors. The best estimate of this will come from the fraction of reference and alternate allele"
  123. + "UMIs that are observed at each snp site. This report can be generated via GatherDigitalAlleleCounts. This is a fractional estimate between 0 and 1. File is tab seperated, with at least 3 columns:"
  124. + "chromosome, position, maf_umi. When supplied and CELL_CONTAMINATION_ESTIMATE_FILE is provided, this modifies the likelihood error rates to take into account how often "
  125. + "the allele observed can be drawn from ambient RNA.", optional = true)
  126. public File ALLELE_FREQUENCY_ESTIMATE_FILE = null;
  127. @Argument(doc = "At least <FRACTION_SAMPLES_PASSING> samples must have genotype scores >= GQ_THRESHOLD for the variant in the VCF to be included in the analysis.")
  128. public double FRACTION_SAMPLES_PASSING = 0.5;
  129. @Argument(doc = "A list of chromosomes to omit from the analysis. The default is to omit the sex chromosomes.")
  130. public List<String> IGNORED_CHROMOSOMES = new ArrayList<>(Arrays.asList("X", "Y", "MT"));
  131. /**
  132. * this produces worse results with in-silico testing, generating more misclassifications of singlets as doublets.
  133. *
  134. * @Argument(doc="For SNPs that don't have a high quality genotype in the VCF, should we infer a global penalty per SNP
  135. * and apply it to donors for that SNP that are not confidently called?") public boolean
  136. * USE_MISSING_DATA=true;
  137. */
  138. private boolean USE_MISSING_DATA = false;
  139. @Argument(doc = "Force evaluation of the doublet at the given mixture ratio. Should be a number between 0 and 1.", optional = true)
  140. public Double FORCED_RATIO = 0.8;
  141. @Argument(doc = "Should cells that were assigned to a donor not on the sample list be tested? When enabled, "
  142. + "this forces the program to load genotype information for all donors seen at least once, even when not on the sample list. If there are many off-target assignments,"
  143. + "this can use large amounts of memory. Set to false to skip testing cells that you'll probably discard as incorrectly assigned later anyway, set to true to "
  144. + "test each cell not on the donor lists against all possible donors on the list.")
  145. public Boolean TEST_UNEXPECTED_DONORS = true;
  146. @Argument(doc = "For each cell, when comparing donor pairs to each other, scale the likelihoods to the number of UMIs. "
  147. + "This provides an additional penalty score to donor pairs with more incomplete information that makes donor pairs more comparable.")
  148. public Boolean SCALE_LIKELIHOODS = true;
  149. @Argument(doc = "EXPERIMENTAL!!! Run the program in DNA Mode. In this mode, reads should have a cell barcode, but will be missing gene annotations and UMIs. All reads will be "
  150. + "accepted as passing, and each read (or read pair) will be treated as a single UMI If the data is PCR Duplicate marked, duplicate reads will be filtered. ")
  151. public Boolean DNA_MODE = false;
  152. @Argument(doc = "Set to false exclude donor cells that are unable to be tested for doublets.")
  153. public Boolean WRITE_ALL_DONOR_BARCODES = true;
  154. @Argument(doc = "The value to write for the best pair p-value for untested cells.")
  155. public Double MISSING_BEST_PAIR_PVALUE = 1.0E-101d;
  156. /*
  157. * @Argument
  158. * (doc="The model tests the best donor against the all the possible second most likely donors to find the pair that best explain the data. Sort the donors by their single donor likelihood score,"
  159. * +
  160. * "and only test the best <MIN_DONOR_PAIRS_TESTED> donors. This can reduce the number of tests / runtime / memory for large pools while producing approximately the same output."
  161. * )
  162. */
  163. private Integer MIN_DONOR_PAIRS_TESTED = Integer.MAX_VALUE;
  164. // @Argument(doc="See MIN_DONOR_PAIRS_TESTED. This limits the number of donors tested to a fraction of the total number
  165. // of donors. The number of donors used is the maximum of FRACTION_DONOR_PAIRS_TESTED and MIN_DONOR_PAIRS_TESTED.")
  166. private Double FRACTION_DONOR_PAIRS_TESTED = 1.0;
  167. private final String SNP_TAG = "YS";
  168. private static DecimalFormat mixtureFormat = new DecimalFormat("#.###");
  169. @Override
  170. protected int doWork() {
  171. if (CELL_CONTAMINATION_ESTIMATE_FILE != null) {
  172. IOUtil.assertFileIsReadable(CELL_CONTAMINATION_ESTIMATE_FILE);
  173. }
  174. if (ALLELE_FREQUENCY_ESTIMATE_FILE != null) {
  175. IOUtil.assertFileIsReadable(ALLELE_FREQUENCY_ESTIMATE_FILE);
  176. }
  177. List<String> donorList = ParseBarcodeFile.readCellBarcodeFile(this.SAMPLE_FILE);
  178. log.info("Number of donors in donor list [" + donorList.size() + "]");
  179. int pairsToTest = getNumberOfDonorsToTest(donorList.size(), this.MIN_DONOR_PAIRS_TESTED, this.FRACTION_DONOR_PAIRS_TESTED);
  180. PrintStream perDonorWriter = null;
  181. if (OUTPUT_ALL_PAIRS != null) {
  182. perDonorWriter = new ErrorCheckingPrintStream(IOUtil.openFileForWriting(this.OUTPUT_ALL_PAIRS));
  183. writeAssignmentHeader(perDonorWriter, pairsToTest, false, true);
  184. }
  185. PrintStream perSNPWriter = null;
  186. if (OUTPUT_PER_SNP != null) {
  187. IOUtil.assertFileIsWritable(this.OUTPUT_PER_SNP);
  188. perSNPWriter = new ErrorCheckingPrintStream(IOUtil.openFileForWriting(this.OUTPUT_PER_SNP));
  189. writePerSNPHeader(perSNPWriter);
  190. }
  191. PrintStream writer = new ErrorCheckingPrintStream(IOUtil.openFileForWriting(this.OUTPUT));
  192. writeAssignmentHeader(writer, pairsToTest, true, false);
  193. final VCFFileReader vcfReader = new VCFFileReader(this.VCF.toPath(), false);
  194. final SamHeaderAndIterator headerAndIter = SamFileMergeUtil.mergeInputPaths(
  195. PicardHtsPath.toPaths(this.INPUT_BAM), false, SamReaderFactory.makeDefault());
  196. AssertSequenceDictionaryIntersection.assertIntersectionObjectVcf(
  197. headerAndIter.header, "BAM INPUT(S)", this.VCF.toPath(), log);
  198. // extract the sequence dictionary to build the interval list.
  199. SAMSequenceDictionary vcfDict = vcfReader.getFileHeader().getSequenceDictionary();
  200. // disable GQ filter if it's not in the header.
  201. if (!VCFUtils.GQInHeader(vcfReader)) {
  202. this.GQ_THRESHOLD = -1;
  203. log.info("Genotype Quality [GQ] not found in header. Disabling GQ_THRESHOLD parameter");
  204. }
  205. CellCollectionSampleLikelihoodCollection cslc = CellCollectionSampleLikelihoodCollection.parseFile(this.SINGLE_DONOR_LIKELIHOOD_FILE);
  206. // A map where the key is the cell barcode, and the value is the best donor.
  207. Map<String, String> bestDonorForCell = getBestDonorForCell(this.SINGLE_DONOR_LIKELIHOOD_FILE);
  208. // Filter the single donor assignment by the donor list if requested.
  209. if (!this.TEST_UNEXPECTED_DONORS)
  210. bestDonorForCell = filterDonorMap(bestDonorForCell, donorList);
  211. // read in the per-cell penalty score and validate against the best donor per cell to make sure you have penalties for
  212. // every donor.
  213. Map<String, Double> contaminationMap = CellAssignmentUtils.getCellContamination(this.CELL_CONTAMINATION_ESTIMATE_FILE, bestDonorForCell.keySet());
  214. Map<Interval, Double> variantMinorAlleleFrequency = CellAssignmentUtils.getMinorAlleleFrequencyMap(this.ALLELE_FREQUENCY_ESTIMATE_FILE);
  215. // all donors is the input set of donors plus any best donor for a cell.
  216. // this list of donors is restricted to the sample list if TEST_UNEXPECTED_DONORS=false.
  217. Set<String> allDonors = new HashSet<>();
  218. allDonors.addAll(donorList);
  219. allDonors.addAll(new HashSet<>(bestDonorForCell.values()));
  220. List<String> allDonorsList = new ArrayList<>(allDonors);
  221. log.info("Number of donors that can be either donor in a donor pair [" + allDonorsList.size() + "]");
  222. // Keep all the barcodes that are in the barcode list AND in the single donor assignments.
  223. List<String> cellBarcodes = getCellBarcodes(this.CELL_BC_FILE, bestDonorForCell, this.TEST_UNEXPECTED_DONORS);
  224. // If there is only one donor at this point, doublet detection should not continue.
  225. if (allDonorsList.size()<2) {
  226. singleDonorGracefulExit(bestDonorForCell, writer, perDonorWriter, perSNPWriter);
  227. return 0;
  228. }
  229. // Pass a list of all donors requested + best calls if TEST_UNEXPECTED_DONORS is true.
  230. // set the %passing to be 0, since the snpIntervals will properly filter on the right set of donors, but you want a
  231. // super-set of donors available
  232. // so if you call donors A-D, but the single cell assigned E, then E would still be an available donor in the genotype
  233. // matrix.
  234. PeekableIterator<VariantContext> vcfIterator = SampleAssignmentVCFUtils.getVCFIterator(vcfReader, allDonorsList, false, this.GQ_THRESHOLD,
  235. this.FRACTION_SAMPLES_PASSING, IGNORED_CHROMOSOMES, log);
  236. GenotypeMatrix genotypeMatrix = new GenotypeMatrix(vcfIterator, this.GQ_THRESHOLD, allDonorsList);
  237. vcfIterator.close();
  238. Map<Interval, Double> genotypeQuality = genotypeMatrix.getAverageGenotypeQuality();
  239. final IntervalList snpIntervals = new IntervalList(vcfDict);
  240. snpIntervals.addall(genotypeMatrix.getSNPIntervals());
  241. // a requested early exit if there are no SNPs.
  242. if (snpIntervals.getIntervals().isEmpty()) {
  243. log.error("No SNP intervals detected! Check to see if your VCF filter thresholds are too restrictive!");
  244. return 1;
  245. }
  246. PeekableIterator<List<SampleGenotypeProbabilities>> sampleGenotypeIterator = prepareIterator(snpIntervals, cellBarcodes, genotypeQuality);
  247. int cellCount = 0;
  248. int reportInterval = 100;
  249. final Set<String> writtenBarcodes = new HashSet<>();
  250. log.info("Calling doublets");
  251. if (!sampleGenotypeIterator.hasNext()) {
  252. log.warn("No Cells found for analysis.");
  253. } else {
  254. while (sampleGenotypeIterator.hasNext()) {
  255. cellCount++;
  256. if (cellCount % reportInterval == 0)
  257. log.info("Tested cell #" + cellCount);
  258. List<SampleGenotypeProbabilities> probs = sampleGenotypeIterator.next();
  259. String cell = probs.get(0).getCell();
  260. String bestDonor = bestDonorForCell.get(cell);
  261. if (bestDonor == null)
  262. throw new IllegalStateException("Cell [" + cell + "] has no best donor assignment in file.");
  263. VariantDataFactory f = null;
  264. f = new VariantDataFactory(cell, probs, genotypeMatrix, FIXED_ERROR_RATE, USE_MISSING_DATA, MAX_ERROR_RATE, contaminationMap,
  265. variantMinorAlleleFrequency);
  266. FindOptimalDonorMixture fodm = new FindOptimalDonorMixture(f);
  267. // AllPairedSampleAssignmentsForCell allAssignments = fodm.findBestDonorPair(bestDonor, donorList, FORCED_RATIO);
  268. // List<String> donorsThisCell = getExpectedSecondDonorsRankedByLikelihood(cell, cslc, pairsToTest, donorList, bestDonor);
  269. AllPairedSampleAssignmentsForCell allAssignments = fodm.findBestDonorPair(bestDonor, donorList, FORCED_RATIO, SCALE_LIKELIHOODS);
  270. SamplePairAssignmentForCell best = allAssignments.getBestAssignment();
  271. // edge case: assignment is null because there's no data for this cell. This only happens
  272. // when a user has a cell selection error or similar and attempts to call cell barcodes that aren't cells.
  273. if (best==null) {
  274. log.warn("No best pair found for cell ["+cell+"] due to no informative UMIs. Cell selection or similar problem?");
  275. best= SamplePairAssignmentForCell.constructEmptyResult(cell, bestDonorForCell.get(cell));
  276. }
  277. double bestPairPvalue = allAssignments.getBestPairPvalue();
  278. writeAssignment(best, bestPairPvalue, writer, false);
  279. if (OUTPUT_ALL_PAIRS != null) {
  280. writeAssignment(allAssignments.getBestAssignment(), null, perDonorWriter, true);
  281. // apply ordering to other assignments for stability of outputs. Sort by 2nd donor name.
  282. Comparator<SamplePairAssignmentForCell> comparator = java.util.Comparator.comparing(SamplePairAssignmentForCell::getSampleTwo,
  283. java.util.Comparator.naturalOrder());
  284. List<SamplePairAssignmentForCell> allOther = allAssignments.getOtherAssignments();
  285. Collections.sort(allOther, comparator);
  286. for (SamplePairAssignmentForCell other : allOther)
  287. writeAssignment(other, null, perDonorWriter, true);
  288. }
  289. reportResultsPerSNP(cell, f, bestDonor, donorList, allAssignments, perSNPWriter);
  290. writtenBarcodes.add(cell);
  291. }
  292. }
  293. if (WRITE_ALL_DONOR_BARCODES) {
  294. final List<String> remainingCellBarcodes = new ArrayList<>(bestDonorForCell.keySet());
  295. remainingCellBarcodes.removeAll(writtenBarcodes);
  296. Collections.sort(remainingCellBarcodes);
  297. for (final String cell : remainingCellBarcodes) {
  298. log.warn("No best pair found for cell [" + cell + "] due to no informative UMIs. Cell selection or similar problem?");
  299. final SamplePairAssignmentForCell best = SamplePairAssignmentForCell.constructEmptyResult(cell, bestDonorForCell.get(cell));
  300. writeAssignment(best, MISSING_BEST_PAIR_PVALUE, writer, false);
  301. }
  302. }
  303. if (OUTPUT_PER_SNP != null)
  304. perSNPWriter.close();
  305. if (OUTPUT_ALL_PAIRS != null)
  306. perDonorWriter.close();
  307. writer.close();
  308. log.info("Finished!");
  309. return 0;
  310. }
  311. /**
  312. * In the strange edge case where there is only a single donor to be tested, write out a default output file instead of going through testing.
  313. * This function additionally closes all potentially open writers and runs logging.
  314. * @param bestDonorForCell A map containing cell barcodes and the best donor for each cell.
  315. * @param writer The file to write to per-cell outputs.
  316. * @param perDonorWriter Closes this file if not null.
  317. * @param perSNPWriter Closes this file if not null.
  318. */
  319. void singleDonorGracefulExit(Map<String, String> bestDonorForCell, PrintStream writer,
  320. PrintStream perDonorWriter, PrintStream perSNPWriter) {
  321. // clean up more detailed file writers.
  322. if (OUTPUT_ALL_PAIRS!=null) perDonorWriter.close();
  323. if (OUTPUT_PER_SNP != null) perSNPWriter.close();
  324. // write a default output per cell close results and quit.
  325. log.error("The donor file only contained a single donor, and no additional donors were detected by single donor assignment. Doublet detection will not continue. "
  326. + "A default output will be written to perserve downstream pipeline functionality.");
  327. writeSingleDonorEdgeCaseOutput(bestDonorForCell, writer);
  328. }
  329. /**
  330. * In the strange edge case where there is only a single donor to be tested, write out a default output file instead of going through testing.
  331. * @param bestDonorForCell A map containing cell barcodes and the best donor for each cell.
  332. * @param writer The file to write to
  333. */
  334. void writeSingleDonorEdgeCaseOutput(Map<String, String> bestDonorForCell, PrintStream writer) {
  335. for (String cell: bestDonorForCell.keySet()) {
  336. SamplePairAssignmentForCell best = SamplePairAssignmentForCell.constructEmptyResult(cell, bestDonorForCell.get(cell));
  337. writeAssignment(best, MISSING_BEST_PAIR_PVALUE, writer, false);
  338. }
  339. writer.close();
  340. }
  341. /**
  342. * Filter the map of cell barcode -> donor to only retain cell barcodes where the assigned donor is in the donor list.
  343. *
  344. * @param bestDonorForCell
  345. * @param donorList
  346. * @return A submap of the input map where all values are contained in the donor list.
  347. */
  348. Map<String, String> filterDonorMap(Map<String, String> bestDonorForCell, List<String> donorList) {
  349. Set<String> dl = new HashSet<String>(donorList);
  350. Map<String, String> result = new HashMap<String, String>();
  351. for (String cellBC : bestDonorForCell.keySet()) {
  352. String donor = bestDonorForCell.get(cellBC);
  353. if (dl.contains(donor))
  354. result.put(cellBC, donor);
  355. }
  356. return result;
  357. }
  358. private void reportResultsPerSNP(final String cell, final VariantDataFactory variantFactory, final String bestDonor, final List<String> vcfSamples,
  359. final AllPairedSampleAssignmentsForCell allAssignments, final PrintStream out) {
  360. if (out == null)
  361. return;
  362. List<String> other = FindOptimalDonorMixture.getNonPrimarySamples(bestDonor, vcfSamples);
  363. for (String o : other) {
  364. VariantDataCollection vdc = variantFactory.getVariantData(bestDonor, o);
  365. SamplePairAssignmentForCell mixtureResult = allAssignments.getAssignmentForDonorPair(bestDonor, o);
  366. List<VariantData> vdList = vdc.getVariantData();
  367. double mixture = mixtureResult.getMixture();
  368. for (VariantData vd : vdList)
  369. writePerSNPReport(cell, vd, bestDonor, o, mixture, out);
  370. }
  371. }
  372. private void writePerSNPReport(final String cell, final VariantData vd, final String sampleOne, final String sampleTwo, final Double mixture,
  373. final PrintStream out) {
  374. /*
  375. * if (mixture==null) { String [] line = {cell, sampleOne, sampleTwo, vd.getSNPInterval().getContig(),
  376. * Integer.toString(vd.getSNPInterval().getStart()), vd.getGenotypeOne().toString(), vd.getGenotypeTwo().toString(),
  377. * Integer.toString(vd.getGenotypeCountReference()), Integer.toString(vd.getGenotypeCountAlternate()), "NA",
  378. * Double.toString(vd.getLogLikelihood(1)), Double.toString(vd.getLogLikelihood(0))}; String h = StringUtils.join(line,
  379. * "\t"); out.println(h); return; }
  380. */
  381. String[] line = { cell, sampleOne, sampleTwo, vd.getSNPInterval().getContig(), Integer.toString(vd.getSNPInterval().getStart()),
  382. vd.getGenotypeOne().toString(), vd.getGenotypeTwo().toString(), Integer.toString(vd.getGenotypeCountReference()),
  383. Integer.toString(vd.getGenotypeCountAlternate()), Double.toString(vd.getLogLikelihood(mixture)), Double.toString(vd.getLogLikelihood(1)),
  384. Double.toString(vd.getLogLikelihood(0)) };
  385. String h = StringUtils.join(line, "\t");
  386. out.println(h);
  387. }
  388. private void writePerSNPHeader(final PrintStream out) {
  389. String[] line = { "cell", "sampleOne", "sampleTwo", "chr", "pos", "genotype_S1", "genotype_S2", "refAlleleCount", "altAlleleCount",
  390. "likelihood_mixture", "likelihood_S1", "likelihood_S2" };
  391. String h = StringUtils.join(line, "\t");
  392. out.println(h);
  393. }
  394. private String convertNullToString(final Double x) {
  395. if (x == null)
  396. return ("NA");
  397. return Double.toString(x);
  398. }
  399. private void writeAssignmentHeader(final PrintStream out, final int pairsToTest, final boolean outputBestPairPvalue, final boolean writeScaledLikelihoods) {
  400. final List<String> paths = FileUtils.toAbsoluteStrings(PicardHtsPath.toPaths(this.INPUT_BAM));
  401. String bamList = StringUtils.join(paths, ",");
  402. final List<String> header = new ArrayList<>(Arrays.asList(
  403. "#INPUT_BAM=" + bamList, "INPUT_VCF=" + FileUtils.toAbsoluteString(this.VCF.toPath()),
  404. "DONOR_FILE=" + this.SAMPLE_FILE, "CELL_BC_FILE=" + CELL_BC_FILE, "GQ_THRESHOLD=" + Integer.toString(this.GQ_THRESHOLD),
  405. "FRACTION_SAMPLES_PASSING=" + Double.toString(this.FRACTION_SAMPLES_PASSING), "FORCED_RATIO=" + convertNullToString(FORCED_RATIO),
  406. "USE_MISSING_DATA=" + USE_MISSING_DATA, "READ_MQ=" + Integer.toString(this.READ_MQ),
  407. "FIXED_ERROR_RATE=" + convertNullToString(this.FIXED_ERROR_RATE), "MAX_ERROR_RATE=" + convertNullToString(this.MAX_ERROR_RATE),
  408. "LOCUS_FUNCTION=" + this.LOCUS_FUNCTION_LIST.toString(), "PAIRS_TO_TEST=" + Integer.toString(pairsToTest)));
  409. if (this.CELL_CONTAMINATION_ESTIMATE_FILE != null && this.ALLELE_FREQUENCY_ESTIMATE_FILE != null) {
  410. header.add("CELL_CONTAMINATION_ESTIMATE_FILE=" + CELL_CONTAMINATION_ESTIMATE_FILE.getAbsolutePath());
  411. header.add("ALLELE_FREQUENCY_ESTIMATE_FILE=" + ALLELE_FREQUENCY_ESTIMATE_FILE.getAbsolutePath());
  412. }
  413. String h = StringUtils.join(header, "\t");
  414. out.println(h);
  415. writeAssignmentColumnNames(out, outputBestPairPvalue, writeScaledLikelihoods);
  416. }
  417. public static void writeAssignmentColumnNames(final PrintStream out, final boolean outputBestPairPvalue, final boolean writeScaledLikelihoods) {
  418. List<String> line = new ArrayList<String>(Arrays.asList("cell", "sampleOneMixtureRatio", "sampleOne", "sampleOneLikelihood", "sampleTwo",
  419. "sampleTwoLikelihood", "mixedSample", "mixedSampleLikelihood", "num_paired_snps", "num_inform_snps", "num_umi", "num_inform_umis",
  420. "lr_test_stat", "sampleOneWrongAlleleCount", "num_homozygous_inform_umis_s1",
  421. "sampleTwoWrongAlleleCount", "num_homozygous_inform_umis_s2", "bestLikelihood", "bestSample", "doublet_pval"));
  422. if (outputBestPairPvalue)
  423. line.add("best_pair_pvalue");
  424. if (writeScaledLikelihoods) {
  425. line.add("bestLikelihoodScaled");
  426. }
  427. String header = StringUtils.join(line, "\t");
  428. out.println(header);
  429. }
  430. public static void writeAssignment(final SamplePairAssignmentForCell assignment, final Double bestPairPvalue, final PrintStream out,
  431. final boolean writeScaledLikelihood) {
  432. String mixture = mixtureFormat.format(assignment.getMixture());
  433. List<String> line = new ArrayList<String>(
  434. Arrays.asList(assignment.getCellBarcode(), mixture, assignment.getSampleOne(), Double.toString(assignment.getSampleOneSingleLikelihood()),
  435. assignment.getSampleTwo(), Double.toString(assignment.getSampleTwoSingleLikelihood()), assignment.getCombinedDonorName(),
  436. Double.toString(assignment.getDoubletLikelihood()), Integer.toString(assignment.getNumSNPs()),
  437. Integer.toString(assignment.getNumInformativeSNPs()), Integer.toString(assignment.getNumUMIs()),
  438. Integer.toString(assignment.getNumInformativeUMIs()), Double.toString(assignment.getDoubletLikelihoodRatio()),
  439. Integer.toString(assignment.getImpossibleAllelesSampleOne()), Integer.toString(assignment.getNumInformativeHomozygousUMIsSampleOne()),
  440. Integer.toString(assignment.getImpossibleAllelesSampleTwo()), Integer.toString(assignment.getNumInformativeHomozygousUMIsSampleTwo()),
  441. Double.toString(assignment.getBestLikelihood()), assignment.getBestSample(), Double.toString(assignment.getDoubletPvalue())));
  442. if (bestPairPvalue != null)
  443. line.add(bestPairPvalue.toString());
  444. if (writeScaledLikelihood)
  445. line.add(Double.toString(assignment.getScaledBestLikelihood()));
  446. String h = StringUtils.join(line, "\t");
  447. out.println(h);
  448. }
  449. public PeekableIterator<List<SampleGenotypeProbabilities>> prepareIterator(final IntervalList snpIntervals, List<String> cellBarcodes, Map<Interval, Double> genotypeQuality) {
  450. SamReaderFactory factory = SamReaderFactory.makeDefault().enable(SamReaderFactory.Option.EAGERLY_DECODE);
  451. SamHeaderAndIterator headerAndIter =
  452. SamFileMergeUtil.mergeInputPaths(PicardHtsPath.toPaths(this.INPUT_BAM), false, factory);
  453. // override the normal gene annotations with new ones before any other operations.
  454. // filter out PCR duplicates.
  455. if (this.DNA_MODE) {
  456. // Replace UMI tags with read names, set strand tag to read tag, set gene function to an accepted on. Overwrites ALL
  457. // reads tags.
  458. IgnoreGeneAnnotationTagger tagger = new IgnoreGeneAnnotationTagger(headerAndIter.iterator, this.GENE_NAME_TAG, this.GENE_STRAND_TAG,
  459. this.GENE_FUNCTION_TAG, this.LOCUS_FUNCTION_LIST, false, this.MOLECULAR_BARCODE_TAG, true);
  460. headerAndIter = new SamHeaderAndIterator(headerAndIter.header, (CloseableIterator<SAMRecord>) tagger.iterator());
  461. final PCRDuplicateFilteringIterator pcrDuplicateFilteringIterator = new PCRDuplicateFilteringIterator(headerAndIter.iterator);
  462. headerAndIter = new SamHeaderAndIterator(headerAndIter.header, pcrDuplicateFilteringIterator);
  463. }
  464. SNPUMIBasePileupIterator sbpi = new SNPUMIBasePileupIterator(headerAndIter, snpIntervals, GENE_NAME_TAG, GENE_STRAND_TAG, GENE_FUNCTION_TAG,
  465. LOCUS_FUNCTION_LIST, STRAND_STRATEGY, this.FUNCTIONAL_STRATEGY, this.CELL_BARCODE_TAG, this.MOLECULAR_BARCODE_TAG, this.SNP_TAG,
  466. GeneFunctionCommandLineBase.DEFAULT_FUNCTION_TAG, this.READ_MQ, false, cellBarcodes, genotypeQuality, SortOrder.CELL_SNP);
  467. final SAMSequenceDictionary dict = snpIntervals.getHeader().getSequenceDictionary();
  468. // gets a SampleGenotypeProbabilities for each cell.
  469. SampleGenotypeProbabilitiesIterator result = new SampleGenotypeProbabilitiesIterator(sbpi, dict, this.EDIT_DISTANCE, SortOrder.CELL_SNP);
  470. // clusters SampleGenotypeProbabilities objects across all cells for a SNP
  471. GroupingIterator<SampleGenotypeProbabilities> groupingIterator = new GroupingIterator<>(result, new Comparator<SampleGenotypeProbabilities>() {
  472. @Override
  473. public int compare(final SampleGenotypeProbabilities o1, final SampleGenotypeProbabilities o2) {
  474. int cmp = o1.getCell().compareTo(o2.getCell());
  475. return cmp;
  476. }
  477. });
  478. PeekableIterator<List<SampleGenotypeProbabilities>> peekableIter = new PeekableIterator<>(groupingIterator);
  479. return (peekableIter);
  480. }
  481. List<String> getCellBarcodes(File cellBarcodeFile, Map<String, String> bestDonorForCell, boolean testUnexpectedDonors) {
  482. List<String> cellBarcodes = ParseBarcodeFile.readCellBarcodeFile(this.CELL_BC_FILE);
  483. int numBarcodes = cellBarcodes.size();
  484. log.info("Number of cell barcodes in input file [" + numBarcodes + "]");
  485. if (!testUnexpectedDonors) {
  486. cellBarcodes.retainAll(bestDonorForCell.keySet());
  487. log.info("Number of cell barcodes after filtering to expected donors [" + cellBarcodes.size() + "]");
  488. double fracRemoved = (double) (numBarcodes - cellBarcodes.size()) / (double) numBarcodes;
  489. log.info("% cell barcodes not assigned to an expected donor [" + new DecimalFormat("0.##").format(fracRemoved * 100) + "%]");
  490. }
  491. return cellBarcodes;
  492. }
  493. /**
  494. * Parse the single donor likelihood file, retrieve the best donor for each cell barcode.
  495. *
  496. * @param singleDonorLikelihoodFile The OUTPUT file produced by AssignCellsToSamples.
  497. * @return A map where the key is the cell barcode, and the value is the best donor.
  498. */
  499. Map<String, String> getBestDonorForCell(final File singleDonorLikelihoodFile) {
  500. Map<String, String> result = new HashMap<>();
  501. CellCollectionSampleLikelihoodCollection cslc = CellCollectionSampleLikelihoodCollection.parseFile(singleDonorLikelihoodFile);
  502. for (String cellBarcode : cslc.getCellBarcodes()) {
  503. String donor = cslc.getLikelihoodCollection(cellBarcode).getBestSampleAssignment().getSample();
  504. result.put(cellBarcode, donor);
  505. }
  506. return (result);
  507. }
  508. // getNumberOfDonorsToTest(donorList.size(), this.MIN_DONOR_PAIRS_TESTED, this.FRACTION_DONOR_PAIRS_TESTED);
  509. private int getNumberOfDonorsToTest(int totalDonorPairs, int minDonorPairs, double fractionDonorPairs) {
  510. int numTest = (int) Math.ceil((double) totalDonorPairs * fractionDonorPairs);
  511. int result = Math.max(numTest, minDonorPairs);
  512. // don't test more than the total number of donors.
  513. // this is for when total donors are < minDonorPairs.
  514. if (result > totalDonorPairs)
  515. result = totalDonorPairs;
  516. log.info("Testing " + Integer.toString(result) + " Donor pairs per cell");
  517. return (result);
  518. }
  519. @Override
  520. protected String[] customCommandLineValidation() {
  521. IOUtil.assertFileIsReadable(this.VCF.toPath());
  522. this.INPUT_BAM = FileListParsingUtils.expandPicardHtsPathList(INPUT_BAM);
  523. IOUtil.assertFileIsWritable(this.OUTPUT);
  524. IOUtil.assertFileIsReadable(this.SAMPLE_FILE);
  525. IOUtil.assertFileIsReadable(this.SINGLE_DONOR_LIKELIHOOD_FILE);
  526. IOUtil.assertFileIsReadable(this.CELL_BC_FILE);
  527. final ArrayList<String> list = new ArrayList<>(1);
  528. if (OUTPUT_ALL_PAIRS != null)
  529. IOUtil.assertFileIsWritable(this.OUTPUT_ALL_PAIRS);
  530. if (OUTPUT_PER_SNP != null)
  531. IOUtil.assertFileIsWritable(this.OUTPUT_PER_SNP);
  532. if (!VCFUtils.hasIndex(this.VCF.toPath()))
  533. list.add("VCF is not indexed! Please index and retry.");
  534. if (this.CELL_CONTAMINATION_ESTIMATE_FILE != null && this.ALLELE_FREQUENCY_ESTIMATE_FILE == null)
  535. list.add("If CELL_CONTAMINATION_ESTIMATE_FILE is supplied, must also supply ALLELE_FREQUENCY_ESTIMATE_FILE");
  536. if (this.CELL_CONTAMINATION_ESTIMATE_FILE == null && this.ALLELE_FREQUENCY_ESTIMATE_FILE != null)
  537. list.add("If ALLELE_FREQUENCY_ESTIMATE_FILE is supplied, must also supply CELL_CONTAMINATION_ESTIMATE_FILE");
  538. if (this.FIXED_ERROR_RATE != null & this.MAX_ERROR_RATE == null)
  539. log.info("Running with a fixed error rate of " + this.FIXED_ERROR_RATE);
  540. if (this.MAX_ERROR_RATE != null && this.CELL_CONTAMINATION_ESTIMATE_FILE == null && this.ALLELE_FREQUENCY_ESTIMATE_FILE == null)
  541. log.info("Running with a maximum cap on error rate of " + this.MAX_ERROR_RATE);
  542. if (FRACTION_SAMPLES_PASSING < 0 | FRACTION_SAMPLES_PASSING > 1)
  543. list.add("FRACTION_SAMPLES_PASSING must be between 0 and 1, value was " + Double.toString(this.FRACTION_SAMPLES_PASSING));
  544. return CustomCommandLineValidationHelper.makeValue(super.customCommandLineValidation(), list);
  545. }
  546. /** Stock main method. */
  547. public static void main(final String[] args) {
  548. System.exit(new DetectDoublets().instanceMain(args));
  549. }
  550. /*
  551. private List<String> getExpectedSecondDonorsRankedByLikelihood(String cellBarcode, CellCollectionSampleLikelihoodCollection cslc, int pairsToTest,
  552. List<String> expectedDonors, String bestDonor) {
  553. // short circuit if the requested number of donors is the same as the number of expected donors IE the non-optimized
  554. // strategy.
  555. if (expectedDonors.size() == pairsToTest)
  556. return expectedDonors;
  557. CellSampleLikelihoodCollection c = cslc.getLikelihoodCollection(cellBarcode);
  558. if (c == null)
  559. throw new IllegalArgumentException("Could not find single donor likelihoods for cell " + cellBarcode);
  560. List<String> rankedDonors = c.getDonorsRankedByAssignmentLikelihood();
  561. // exclude the best donor from the ranked list. We don't want to select that.
  562. rankedDonors.remove(bestDonor);
  563. Set<String> expected = new HashSet<String>(expectedDonors);
  564. // filter ranked donors by expected.
  565. List<String> rankedExpectedDonors = rankedDonors.stream().filter(x -> expected.contains(x)).collect(Collectors.toList());
  566. // if pairs to test is set too high somehow (the sample lists doesn't match up with the input single donor assignments)
  567. // then limit the return.
  568. if (rankedExpectedDonors.size() < pairsToTest) {
  569. pairsToTest = rankedExpectedDonors.size();
  570. }
  571. // get the top <X> donors.
  572. rankedExpectedDonors = rankedExpectedDonors.subList(0, pairsToTest);
  573. return rankedExpectedDonors;
  574. }
  575. */
  576. }

DetectDoublets.java at commit d14776a, under MIT · at the source

Overview

Authors: Ricardo CH del Rosario1,2, Fenna M Krienen1,2, Qiangge Zhang2,3, Melissa Goldman1,2, Curtis Mello1,2, Alyssa Lutservitz1,2, Kiku Ichihara1,2, Alec Wysoker1,2, James Nemesh1,2, Guoping Feng2,3, Steven A McCarroll1,2,4
  1. Department of Genetics, Harvard Medical School Boston United States
  2. Stanley Center for Psychiatric Research, Broad Institute of MIT and Harvard Cambridge United States
  3. McGovern Institute for Brain Research, Department of Brain and Cognitive Sciences, Massachusetts Institute of Technology Cambridge United States
  4. Howard Hughes Medical Institute Boston United States
Institutions: Harvard University (United States); Broad Institute (United States); Massachusetts Institute of Technology (United States); Howard Hughes Medical Institute (United States)
Journal: eLife, volume 13, article RP93640
Dates: published online 14 August 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.7554/elife.93640 · PMID 42598888 · PMCID PMC13476068 · OpenAlex W4393112346
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: genetics / omics (modality), non-human primate (organism), cellular / molecular (subfield)
Methods: Statistics, Smoothing, state filtering, decompositions, Connectivity, Machine learning, fMRI & imaging
Keywords: Callithrix jacchus, sibling chimerism, microglia, marmosets, Other
MeSH: Callithrix*, Chimerism*, Microglia*, Animals, Brain, Female, Polymorphism, Single Nucleotide, Siblings (* major topic)
Journal subjects: Genetics and Genomics
Topic: Neuroinflammation and Neurodegeneration Mechanisms (Neurology, Neuroscience), according to OpenAlex
Funding: National Institutes of Health (U01MH114819); Stanley Center for Psychiatric Research, Broad Institute; James and Patricia Poitras Center for Psychiatric Disorders Research at MIT; Hock E. Tan and K. Lisa Yang Center for Autism Research at MIT
Citations: cited by 2 papers (Europe PMC); 34 references in the paper

Abstract

Chimerism happens rarely among most mammals, but is common in marmosets and tamarins, a result of fraternal twin or triplet birth patterns in which in utero connected circulatory systems (through which stem cells transit) lead to persistent blood chimerism (12–80%) throughout life. The presence of Y-chromosome DNA sequences in organs of female marmosets has long suggested that chimerism might also affect these organs. However, a longstanding question is whether this chimerism is driven by blood-derived cells or involves contributions from other cell types. To address this question, we analyzed single-cell RNA-seq data from blood, liver, kidney, and many brain regions across a number of marmosets, using transcribed single-nucleotide polymorphisms (SNPs) to identify cells with the sibling’s genome in various cell types within these tissues. Sibling-derived chimerism in all tissues arose entirely from cells of hematopoietic origin (i.e., myeloid and lymphoid lineages). In brain tissue this was reflected as sibling-derived chimerism among microglia (20–52%) and macrophages (18–64%) but not among other resident cell types (neurons, glia, or ependymal cells). The percentage of microglia that were sibling-derived showed significant variation across brain regions, even within individual animals, likely reflecting distinct responses by genetic-sibling microglia to local recruitment or proliferation cues or, potentially, distinct clonal expansion histories in different brain areas. In the animals and tissues we analyzed, microglial gene expression profiles bore a much stronger relationship to local/host context than to sibling genetic differences. Naturally occurring marmoset chimerism will provide new ways to recognize the effects of genes, mutations, and brain contexts on microglial biology and to distinguish between effects of microglia and other cell types on brain phenotypes.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repositories

Its files are read in the Code ↔ Paper reader above, with 6 matches between paragraphs and lines of code.

broadinstitute/Drop-seq

License: MIT
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: d14776a599bbc401ef2d6ce5c6f96b23d0424cdf, 17 September 2026
Languages: Java (626), Python (58), R (19), Shell (14), Jupyter (1)
Size: 1,409 files, 718 scripts
Software Heritage: archived
Found in: “Software availability”
Holds: README, license file, environment (src/python/pyproject.toml, src/docker/java/Dockerfile, src/docker/PEER/Dockerfile, src/docker/python/Dockerfile, src/docker/R/Dockerfile, src/R/packages/DropSeq.dropulation/DESCRIPTION, src/R/packages/DropSeq.eqtl.susie/DESCRIPTION, src/R/packages/DropSeq.eqtl/DESCRIPTION, src/R/packages/DropSeq.utilities/DESCRIPTION), tests, continuous integration, documentation, 1 notebook
Not found: CITATION.cff
Tools: pandas (25 files), anndata (11 files), data.table (8 files), NumPy (8 files), SciPy (8 files), Scanpy (5 files), ggplot2 (3 files), Matplotlib (2 files), cowplot (1 file), scikit-learn (1 file), tidyverse (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
720 files

lh3/bwa

License: GPL-3.0
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: d82444c17edc2384420409f85557c6ae84019732, 7 August 2026
Languages: C (36), C/C++ (24), JavaScript (3), Perl (2), Shell (1)
Size: 80 files, 66 scripts
Software Heritage: archived
Found in: “Software availability”
Holds: README, license file, continuous integration
Not found: CITATION.cff, environment file, tests, documentation
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
68 files
At the source: github.com/lh3/bwa

satijalab/seurat

License: other
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 586015abde10618ecb32d3fe632267a83317a08d, 21 September 2026
Languages: R (114), C++ (8), C/C++ (4), C (1)
Size: 455 files, 127 scripts
Software Heritage: archived
Found in: “Software availability”
Holds: README, license file, environment (DESCRIPTION), tests, continuous integration, documentation, 70 notebooks
Not found: CITATION.cff
Tools: Seurat (75 files), ggplot2 (48 files), patchwork (27 files), tidyverse (19 files), cowplot (10 files), reshape2 (3 files), SingleCellExperiment (3 files), Plotly (2 files), data.table (1 file), DESeq2 (1 file), Harmony (1 file), igraph (1 file), limma (1 file), Monocle 3 (1 file), reticulate (1 file), UMAP (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
130 files

IanevskiAleksandr/sc-type

License: GPL-3.0
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 630e15cf1e51f2612eda4ad0406dfb17503fa8c9, 10 November 2024
Languages: JavaScript (6), R (4)
Size: 117 files, 10 scripts
Software Heritage: not archived
Found in: “Software availability”
Holds: README, license file
Not found: CITATION.cff, environment file, tests, continuous integration, documentation
Tools: Seurat (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
12 files

satijalab/sctransform

License: GPL-3.0
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 49e35b5aeb76a602910207cbfde1561093340be3, 10 January 2026
Languages: R (28), C++ (2)
Size: 79 files, 30 scripts
Software Heritage: archived
Found in: “Software availability”
Holds: README, license file, environment (DESCRIPTION), tests, continuous integration, documentation, 11 notebooks
Not found: CITATION.cff
Tools: ggplot2 (12 files), reshape2 (11 files), tidyverse (8 files), Seurat (7 files), patchwork (4 files), cowplot (2 files), Plotly (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
32 files

PMBio/peer

License: GPL-2.0
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 40bc4b2cd92459ce42f44dfe279717436395f3f6, 8 May 2012
Languages: C++ (869), C/C++ (648), Python (29), Shell (23), R (8), C (2)
Size: 2,526 files, 1,579 scripts
Software Heritage: not archived
Found in: “Software availability”
Holds: README, license file, environment (python/setup.py, R/peer/DESCRIPTION), documentation
Not found: CITATION.cff, tests, continuous integration
Tools: NumPy (2 files), SciPy (2 files), Matplotlib (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
1,582 files
At the source: github.com/PMBio/peer

Software availability

All software used in the analysis are publicly available. Drop-seq (analysis of snRNA-seq data, clustering, marker genes), Census-seq (estimation of chimerism in WGS data), and Dropulation analysis (estimation of chimerism in snRNA-seq data): https://github.com/broadinstitute/Drop-seq, Broad Institute, 2026; alignment and variant detection of Illumina WGS data: bwa (https://github.com/lh3/bwa, Li, 2026), GATK (https://gatk.broadinstitute.org), BCFtools (https://github.com/samtools/bcftools, Danecek, 2026), samtools (http://www.htslib.org/download), Picard Tools (https://broadinstitute.github.io/picard); R environment (https://www.rstudio.com/products/rstudio/download and https://www.r-project.org); single-cell analysis in R: Seurat (https://github.com/satijalab/seurat, Butler, 2026), cell type identification: scType (https://github.com/IanevskiAleksandr/sc-type, Aleksandr, 2024), SCT transform (https://github.com/satijalab/sctransform/, Hafemeister, 2026); PEER latent factor analysis (https://github.com/PMBio/peer, PMBio, 2012).

Reproduced under the paper's license (CC BY), from the paper cited above.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 6 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 2,530 scripts, each with its path and the digest of its content;
  • 6 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

No dataset and no data link were found in the paper.

Data availability

Brain snRNA-seq of 6 marmosets (CJ022, CJ023, CJ025, CJ026, CJ027, CJ028) were generated as part of the NIH's Brain Initiative Cell Census Network (BICCN) project, while brain snRNA-seq of 5 marmosets (CJ001, CJ006, CJ007, CJ023, CJ102), and all blood, liver, and kidney snRNA-seq were generated for this project. All snRNA-seq datasets are available in the BICCN NeMO portal (https://assets.nemoarchive.org/dat-hsgdsgu and https://assets.nemoarchive.org/dat-1je0mn3). The raw whole-genome sequencing datasets are available from the NIH Sequence Read Archive, under accession number BioProject PRJNA1068102.

The following datasets were generated:

del RosarioR 2023Marmosets Have their Birth Sibling's MicrogliaNeMOnemo:dat-hsgdsgu

Oregon Health and Science University 2024MCC Genome SequencingNCBI BioProjectPRJNA1068102

The following previously published datasets were used:

KrienenF 2023A marmoset brain cell census reveals persistent influence of developmental origin on neuronsNeMOnemo:dat-1je0mn3

del RosarioR 2023WGS of Marmoset Colony at Broad Institute by Stanley Center for Psychiatric ResearchNCBI Sequence Read ArchiveSRX32815946

Reproduced under the paper's license (CC BY), from the paper cited above.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, pages, dates, 11 authors, 5 keywords, 8 MeSH terms, 4 funders, 27 references.

Cite

This paper

del Rosario, R. C., Krienen, F. M., Zhang, Q., Goldman, M., Mello, C., Lutservitz, A., Ichihara, K., Wysoker, A., Nemesh, J., Feng, G., & McCarroll, S. A. (2026). Sibling chimerism among microglia in marmosets. eLife, 13, RP93640. https://doi.org/10.7554/elife.93640

BibTeX

@article{delrosario2026sibling,
author = {del Rosario, Ricardo CH and Krienen, Fenna M and Zhang, Qiangge and Goldman, Melissa and Mello, Curtis and Lutservitz, Alyssa and Ichihara, Kiku and Wysoker, Alec and Nemesh, James and Feng, Guoping and McCarroll, Steven A},
title = {{Sibling chimerism among microglia in marmosets}},
journal = {eLife},
year = {2026},
month = aug,
volume = {13},
pages = {RP93640},
publisher = {eLife Sciences Publications, Ltd},
issn = {2050-084X},
doi = {10.7554/elife.93640},
url = {https://doi.org/10.7554/elife.93640},
pmid = {42598888},
pmcid = {PMC13476068}
}

RIS

TY - JOUR
AU - del Rosario, Ricardo CH
AU - Krienen, Fenna M
AU - Zhang, Qiangge
AU - Goldman, Melissa
AU - Mello, Curtis
AU - Lutservitz, Alyssa
AU - Ichihara, Kiku
AU - Wysoker, Alec
AU - Nemesh, James
AU - Feng, Guoping
AU - McCarroll, Steven A
TI - Sibling chimerism among microglia in marmosets
T2 - eLife
J2 - Elife
PY - 2026
DA - 2026/08/14
VL - 13
SP - RP93640
SN - 2050-084X
PB - eLife Sciences Publications, Ltd
DO - 10.7554/elife.93640
UR - https://doi.org/10.7554/elife.93640
LA - en
ER -

CSL-JSON

{
"id": "10.7554/elife.93640",
"type": "article-journal",
"title": "Sibling chimerism among microglia in marmosets",
"container-title": "eLife",
"author": [
{
"family": "del Rosario",
"given": "Ricardo CH"
},
{
"family": "Krienen",
"given": "Fenna M"
},
{
"family": "Zhang",
"given": "Qiangge"
},
{
"family": "Goldman",
"given": "Melissa"
},
{
"family": "Mello",
"given": "Curtis"
},
{
"family": "Lutservitz",
"given": "Alyssa"
},
{
"family": "Ichihara",
"given": "Kiku"
},
{
"family": "Wysoker",
"given": "Alec"
},
{
"family": "Nemesh",
"given": "James"
},
{
"family": "Feng",
"given": "Guoping"
},
{
"family": "McCarroll",
"given": "Steven A"
}
],
"container-title-short": "Elife",
"volume": "13",
"page": "RP93640",
"DOI": "10.7554/elife.93640",
"PMID": "42598888",
"PMCID": "PMC13476068",
"ISSN": "2050-084X",
"publisher": "eLife Sciences Publications, Ltd",
"URL": "https://doi.org/10.7554/elife.93640",
"language": "en",
"issued": {
"date-parts": [
[
2026,
8,
14
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1038/s44318-026-00818-9 [code]
FAM134B-mediated ER-phagy degrades APP and suppresses Alzheimer's disease pathology.
Journal: The EMBO journal
In common: Monocle 3, Harmony, SingleCellExperiment, 20 other tools, cellular / molecular
[2] doi:10.1016/j.xcrm.2026.102766 [code]
A longitudinal single-cell and spatial multiomic atlas of pediatric high-grade glioma.
Journal: Cell reports. Medicine
In common: Monocle 3, Harmony, SingleCellExperiment, 19 other tools, genetics / omics, cellular / molecular
[3] doi:10.1002/imt2.70163 [code]
Spatial multi-omics unveils sphingolipid metabolic reprogramming within the retinal pathological niche.
Journal: iMeta
In common: Monocle 3, Harmony, SingleCellExperiment, 18 other tools, genetics / omics, cellular / molecular
[4] doi:10.1016/j.xcrm.2026.102651 [code]
Integrative CSF profiling identifies disease-specific immune responses in leptomeningeal disease.
Journal: Cell reports. Medicine
In common: Harmony, SingleCellExperiment, reticulate, 18 other tools, genetics / omics, cellular / molecular
[5] doi:10.1038/s41586-026-10214-2 [code]
Multidimensional profiling of heterogeneity in supratentorial ependymomas.
Journal: Nature
In common: Monocle 3, Harmony, SingleCellExperiment, 17 other tools, genetics / omics
[6] doi:10.1002/ctm2.70683 [code]
Niacin promotes motor function recovery after spinal cord injury via Hcar2-dependent microglia immunometabolic regulation.
Journal: Clinical and translational medicine
In common: Monocle 3, Harmony, SingleCellExperiment, 13 other tools, non-human primate, genetics / omics, cellular / molecular
[7] doi:10.1186/s12974-026-03838-8 [code]
Acarbose modulates microglial Pkm2 acetylation to reshape immunometabolism and preserve retinal neurons after ischemia-reperfusion.
Journal: Journal of neuroinflammation
In common: Monocle 3, Harmony, SingleCellExperiment, 13 other tools, genetics / omics, cellular / molecular
[8] doi:10.1038/s41420-026-02971-w [code]
Multi-omics reveals heterogeneity and functional populations of oligodendrocyte progenitor cells induced by human neural stem cells.
Journal: Cell death discovery
In common: Monocle 3, Harmony, SingleCellExperiment, 13 other tools, genetics / omics, cellular / molecular
[9] doi:10.1038/s41467-026-73305-8 [code]
Comparative analysis of the cellular landscape in mammalian striatum.
Journal: Nature communications
In common: Harmony, SingleCellExperiment, anndata, 12 other tools, non-human primate, genetics / omics, cellular / molecular, 3 references
[10] doi:10.1038/s41467-026-76675-1 [code]
Long-read proteogenomic atlas of human neuronal differentiation reveals isoform diversity informing neurodevelopmental risk mechanisms.
Journal: Nature communications
In common: Monocle 3, reticulate, limma, 16 other tools, genetics / omics

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.