Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions main/html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
<link rel="stylesheet" type="text/css" href="../css/style.css" />
<script src="../js/fillSelectBoxes.js"></script>
<script src="../js/dataAcquisition/fetch.js"></script>
<script src="../js/dataAcquisition/getDataFromSelectedPieSectors.js" defer></script>
<script src="../js/dataAcquisition/getDataFromSelectedFeatures.js" defer></script>
<script src="../js/dataProcessing/mergeExpression.js" defer></script>
<script src="../js/plots/createHeatmap.js" defer></script>
<script src="../js/plots/createViolinPlot.js" defer></script>
Expand Down Expand Up @@ -127,7 +127,7 @@

<!-- Data exploration div -->
<div class="row" id="dataexploration">
<script type="text/javascript" src="../js/plots/createPieCharts.js"></script>
<script type="text/javascript" src="../js/plots/createDataExplorePlots.js"></script>
</div>

<div class="row">
Expand Down
32 changes: 11 additions & 21 deletions main/js/afterSubmit.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,35 +56,21 @@ const buildPlots = async function() {
// GET EXPRESSION DATA:

const selectedGene1 = $(".geneOneMultipleSelection").select2("data").map((gene) => gene.text);
// Find intersecting barcodes based on Mutation/Clinical Pie Chart selections
const pieSectorBarcodes = await getBarcodesFromSelectedPieSectors(selectedTumorTypes);
const histogramBarcodes = await getBarcodesFromSelectedHistogramRange(selectedTumorTypes);
console.log(pieSectorBarcodes, histogramBarcodes);
let intersectedBarcodes;
if(pieSectorBarcodes && histogramBarcodes) {
intersectedBarcodes = pieSectorBarcodes.reduce((acc, curr) => {
if (histogramBarcodes.includes(curr)) {
acc.push(curr);
}
return acc;
}, []);
} else if (pieSectorBarcodes) {
intersectedBarcodes = pieSectorBarcodes;
} else if (histogramBarcodes) {
intersectedBarcodes = histogramBarcodes;
} else {
intersectedBarcodes = null;
}
console.log(intersectedBarcodes);

let cacheGe = await getCacheGE(); // Instantiate cache interface for gene expression
let expressionData;

// GET CLINICAL DATA:
// Get clinical data for either intersected barcodes or entire cohort
let clinicalData;
let cacheBar = await getCacheBAR(); // Instantiate cache interface for barcodes
let barcodesByCohort = await cacheBar.fetchWrapperBAR(selectedTumorTypes); // Fetch all barcodes for selected cohorts
let cacheClin = await getCacheCLIN(); // Instantiate cache interface for clinical data
if (intersectedBarcodes && intersectedBarcodes.length) {

let intersectedBarcodes = await getBarcodesFromSelectedFeatures(selectedTumorTypes);

if (intersectedBarcodes.length > 0) {

// If intersectedBarcodes is populated, then iterate over each cohort's barcodes and filter by the barcodes of interest
for(let index = 0; index < barcodesByCohort.length; index++) {
let obj = barcodesByCohort[index];
Expand All @@ -99,6 +85,9 @@ const buildPlots = async function() {
// Pass in barcodes from expressionData
clinicalData = await cacheClin.fetchWrapperCLIN(selectedTumorTypes, barcodesByCohort); // Fetch clinical data from cache
}
expressionData = (expressionData || []).filter(
r => r && allSelectedGenes.includes(r.gene)
);
cache.set('rnaSeq', 'expressionData', expressionData); // Set localStorage entry for expression data
clinicalData = clinicalData.map(obj => obj.clinical_data); // Extract clinical_data property from each element
clinicalData = clinicalData.flat(); // Flatten clinicalData into a 1-D array
Expand All @@ -110,6 +99,7 @@ const buildPlots = async function() {
let mutationAndClinicalData = mergeClinicalAndMutationData(selectedGene1, mutationData, clinicalData); // Combine mutation data and clinical data into single array of JSON objects
localStorage.setItem("mutationAndClinicalData", JSON.stringify(mutationAndClinicalData));
localStorage.setItem("mutationAndClinicalFeatureKeys", Object.keys((mutationAndClinicalData[0])).sort());

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
buildHeatmap(expressionData, mutationAndClinicalData);
buildViolinPlot(allSelectedGenes, expressionData);
Expand Down
192 changes: 192 additions & 0 deletions main/js/dataAcquisition/getDataFromSelectedFeatures.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@

// ***** Get intersection of barcodes from selections in data explore charts (below) *****

getBarcodesFromSelectedFeatures = async function(selectedTumorTypes) {

// retrieve selection data from global variables
let selectedCategoricalFields = Object.keys(selectedCategoricalFeatures);
let selectedContinuousFields = Object.keys(selectedContinuousFeatures);

let barcodesReppingAllSelectionsForEachFeature = [];
let cacheMu = await getCacheMU(); // Instantiate caching interface for mutation data
let cacheBar = await getCacheBAR(); // Instantiate caching interface for barcode data
let cacheClin = await getCacheCLIN(); // Instantiate caching interface for clinical data

let allBarcodesForSelectedTumorType = await cacheBar.fetchWrapperBAR(selectedTumorTypes); // Get all barcodes for the selected tumor type(s)

let allClinicalDataForSelectedTumorTypes = await cacheClin.fetchWrapperCLIN(selectedTumorTypes, allBarcodesForSelectedTumorType); // Fetch clinical data for all patients with selected tumor type(s)
allClinicalDataForSelectedTumorTypes = allClinicalDataForSelectedTumorTypes.map(obj => obj.clinical_data); // Extract mutation_data property for each cohort
allClinicalDataForSelectedTumorTypes = allClinicalDataForSelectedTumorTypes.flat(); // Use flat() to make patients' clinical data a 1-D array

// LOOP THRU ALL CLICKED ~CATEGORICAL~ FIELDS (whether from gene mutation plot or metadata)
// (e.g., one gene at a time, or one metadata field at a time)
for(let i = 0; i < selectedCategoricalFields.length; i++) {

let currentField = selectedCategoricalFields[i];

// if current selected sector belongs to a gene...
if(currentField[i].toUpperCase() == currentField[i]) {

let currentGene = currentField;

let allMutationDataForThisGene = await cacheMu.fetchWrapperMU(selectedTumorTypes, [currentGene]); // Fetch all mutation data for currentGene

let clickedMutations = selectedCategoricalFeatures[currentGene]; // Get array of selected mutations

barcodesReppingAllSelectionsForEachFeature[currentGene+'mutationFilt'] = []; // Initialize to empty array to use push()

if(clickedMutations.length > 0) {
barcodesReppingAllSelectionsForEachFeature[currentGene+'mutationFilt'] = allMutationDataForThisGene
.filter(record => clickedMutations.includes(record.mutation_label))
.map(record => record.tcga_participant_barcode);

}
// We are basically getting lists of barcodes, repping ppl who have particular mutations in particular genes

// If no mutations have been selected, then we will append all the barcodes to the array
// else {
// barcodesReppingAllSelectionsForEachFeature[currentGene] = allMutationDataForThisGene.map(record => record.tcga_participant_barcode);
// }

// ELSE, CURRENT CATEGORICAL FIELD IS A "CLINICAL" ONE
} else {

let currentClinicalFeature = currentField;
let clickedClinicalValues = selectedCategoricalFeatures[currentClinicalFeature];

let filteredClinicalData = [];
let uniqueBarcodes;

function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
// for each value of the
for(let j = 0; j < clickedClinicalValues.length; j++) {

let currentClinicalValue = clickedClinicalValues[j];

// filter the clinical data object to only include entries (i.e., barcodes) that have the selected values for the clinical data field
filteredClinicalData = allClinicalDataForSelectedTumorTypes.filter(person => (person[currentClinicalFeature] == currentClinicalValue))
// now just get the barcodes from those entries
let onlyBarcodes = filteredClinicalData.map(x => x.tcga_participant_barcode);
uniqueBarcodes = onlyBarcodes.filter(onlyUnique);

if (barcodesReppingAllSelectionsForEachFeature[currentClinicalFeature] === undefined)
barcodesReppingAllSelectionsForEachFeature[currentClinicalFeature] = uniqueBarcodes;
else
barcodesReppingAllSelectionsForEachFeature[currentClinicalFeature] = barcodesReppingAllSelectionsForEachFeature[currentClinicalFeature].concat(uniqueBarcodes);
}
}
}

// LOOP THRU ALL CLICKED ~CONTINUOUS~ FIELDS (whether from gene mutation plot or metadata)
// (e.g., one gene at a time, or one metadata field at a time)
for(let i = 0; i < selectedContinuousFields.length; i++) {

let currentField = selectedContinuousFields[i];

let rangeValue = selectedContinuousFeatures[currentField]; // Get range of data to filter allClinicalDataForSelectedTumorTypes by
let onlyBarcodes = [];

// if current continuous field is a GENE
if (currentField[0] === currentField[0].toUpperCase()) {

// Fetch gene expression cache
let cacheGe = await getCacheGE();

let genesWithExpressionFilters = Object.keys(selectedContinuousFeatures).filter(key => {
// Check if this key represents a gene (starts with uppercase) and has a range
return key[0] === key[0].toUpperCase() &&
selectedContinuousFeatures[key] &&
Array.isArray(selectedContinuousFeatures[key]) &&
selectedContinuousFeatures[key].length >= 2;
});
// Process each gene with expression filters
for (let gene of genesWithExpressionFilters) {
let rangeValue = selectedContinuousFeatures[gene];
let minExpression = rangeValue[0];
let maxExpression = rangeValue[1];

// console.log(`Processing expression filter for ${gene}: ${minExpression} to ${maxExpression}`);

// Fetch gene expression data for this gene
let geneExpressionData = await cacheGe.fetchWrapperGE(selectedTumorTypes, [gene]);

// Filter by expression range and tumor samples only
let filteredExpressionData = geneExpressionData.filter(record => {
return record.sample_type === "TP" &&
record.expression_log2 !== null &&
record.expression_log2 !== undefined &&
!isNaN(record.expression_log2) &&
record.expression_log2 >= minExpression &&
record.expression_log2 <= maxExpression;
});


// Extract unique barcodes for this gene
onlyBarcodes = filteredExpressionData.map(record => record.tcga_participant_barcode);

barcodesReppingAllSelectionsForEachFeature[gene+'expressionFilt'] = onlyBarcodes

}

} else {

filteredRangeData = allClinicalDataForSelectedTumorTypes.filter(person => (person[currentField] >= rangeValue[0] && person[currentField] <= rangeValue[1]))
onlyBarcodes = filteredRangeData.map(x => x.tcga_participant_barcode);
barcodesReppingAllSelectionsForEachFeature[currentField] = onlyBarcodes

}
}

// console.log(barcodesReppingAllSelectionsForEachFeature)

function intersectValues(kv) {
let arrays;

if (kv instanceof Map) {
arrays = Array.from(kv.values());
} else if (Array.isArray(kv)) {
// Handle arrays with named properties (Object.values grabs them)
const vals = Object.values(kv);
// If entries-like [[key, arr], ...], pick the arr
if (vals.every(v => Array.isArray(v) && v.length === 2 && Array.isArray(v[1]))) {
arrays = vals.map(v => v[1]);
} else {
arrays = vals.filter(Array.isArray); // named props or array-of-arrays
if (arrays.length === 0) arrays = kv.filter?.(Array.isArray) ?? [];
}
} else if (kv && typeof kv === 'object') {
arrays = Object.values(kv).filter(Array.isArray);
} else {
return [];
}

if (!arrays.length || arrays.some(a => a.length === 0)) return [];

// Dedupe each, start with smallest, intersect via Sets
const deduped = arrays.map(a => {
const s = new Set(); const out = [];
for (const x of a) if (!s.has(x)) { s.add(x); out.push(x); }
return out;
}).sort((a,b) => a.length - b.length);

const [first, ...rest] = deduped;
const restSets = rest.map(a => new Set(a));

const out = [];
const seen = new Set();
for (const x of first) {
if (!seen.has(x) && restSets.every(s => s.has(x))) {
seen.add(x);
out.push(x);
}
}
return out;
}

const common = intersectValues(barcodesReppingAllSelectionsForEachFeature);
// console.log(common);

return common
}
Loading