bioinformatics / index.html
Dobator's picture
undefined - Initial Deployment
3211546 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bioinformatics Toolkit</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
.tool-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
}
.file-upload {
border: 2px dashed #cbd5e0;
transition: all 0.3s ease;
}
.file-upload:hover {
border-color: #4f46e5;
}
.file-upload.dragover {
border-color: #4f46e5;
background-color: #f0f7ff;
}
#volcanoPlot {
width: 100%;
height: 500px;
}
.gene-item:hover {
background-color: #f3f4f6;
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<!-- Navigation -->
<nav class="bg-indigo-600 text-white shadow-lg">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16 items-center">
<div class="flex items-center">
<div class="flex-shrink-0 flex items-center">
<svg class="h-8 w-8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M20.5 11H19V7c0-1.1-.9-2-2-2h-4V3.5a2.5 2.5 0 00-5 0V5H4c-1.1 0-1.99.9-1.99 2v3.8H3.5c1.49 0 2.7 1.21 2.7 2.7 0 1.49-1.21 2.7-2.7 2.7H2V20c0 1.1.9 2 2 2h3.8v-1.5c0-1.49 1.21-2.7 2.7-2.7 1.49 0 2.7 1.21 2.7 2.7V22H17c1.1 0 2-.9 2-2v-4h1.5a2.5 2.5 0 000-5z" />
</svg>
<span class="ml-2 text-xl font-bold">BioTools</span>
</div>
</div>
<div>
<button id="homeBtn" class="px-3 py-2 rounded-md text-sm font-medium bg-indigo-700">Home</button>
</div>
</div>
</div>
</nav>
<!-- Main Content -->
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- Home Page -->
<div id="homePage" class="space-y-8">
<div class="text-center">
<h1 class="text-4xl font-bold text-gray-900 mb-2">Bioinformatics Toolkit</h1>
<p class="text-xl text-gray-600">A collection of tools for biological data analysis</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Volcano Plot Tool Card -->
<div class="bg-white rounded-lg shadow-md overflow-hidden tool-card transition-all duration-300 cursor-pointer" id="volcanoToolCard">
<div class="p-6">
<div class="flex items-center">
<div class="flex-shrink-0 bg-indigo-100 p-3 rounded-lg">
<svg class="h-8 w-8 text-indigo-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
</svg>
</div>
<div class="ml-4">
<h3 class="text-lg font-medium text-gray-900">Volcano Plot</h3>
<p class="text-gray-500">Visualize differential gene expression</p>
</div>
</div>
</div>
</div>
<!-- Enrichment Tool Card -->
<div class="bg-white rounded-lg shadow-md overflow-hidden tool-card transition-all duration-300 cursor-pointer" id="enrichmentToolCard">
<div class="p-6">
<div class="flex items-center">
<div class="flex-shrink-0 bg-green-100 p-3 rounded-lg">
<svg class="h-8 w-8 text-green-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
</svg>
</div>
<div class="ml-4">
<h3 class="text-lg font-medium text-gray-900">Gene Enrichment</h3>
<p class="text-gray-500">Analyze DEGs for pathway enrichment</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Volcano Plot Tool -->
<div id="volcanoToolPage" class="hidden bg-white rounded-lg shadow-md p-6 space-y-6">
<div class="flex items-center">
<button id="volcanoBackBtn" class="mr-4 p-2 rounded-full hover:bg-gray-100">
<svg class="h-6 w-6 text-gray-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg>
</button>
<h2 class="text-2xl font-bold text-gray-900">Volcano Plot Tool</h2>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="lg:col-span-1 space-y-4">
<div class="file-upload p-8 rounded-lg text-center cursor-pointer" id="volcanoFileUpload">
<input type="file" id="volcanoFileInput" class="hidden" accept=".csv,.tsv,.txt">
<svg class="mx-auto h-12 w-12 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
</svg>
<h3 class="mt-2 text-sm font-medium text-gray-900">Upload your data</h3>
<p class="mt-1 text-sm text-gray-500">CSV, TSV or TXT file with gene expression data</p>
<p class="mt-1 text-xs text-gray-500">(symbol, log2FoldChange, padj columns required)</p>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<h3 class="text-sm font-medium text-gray-900 mb-2">Plot Settings</h3>
<div class="space-y-3">
<div>
<label for="logFCCutoff" class="block text-sm font-medium text-gray-700">logFC Cutoff</label>
<input type="number" id="logFCCutoff" value="1" step="0.1" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm">
</div>
<div>
<label for="pValueCutoff" class="block text-sm font-medium text-gray-700">p-value Cutoff</label>
<input type="number" id="pValueCutoff" value="0.05" step="0.01" min="0" max="1" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm">
</div>
<button id="generateVolcanoBtn" class="w-full bg-indigo-600 text-white py-2 px-4 rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Generate Plot
</button>
</div>
</div>
</div>
<div class="lg:col-span-2">
<div class="bg-gray-50 p-4 rounded-lg h-full">
<h3 class="text-sm font-medium text-gray-900 mb-2">Volcano Plot</h3>
<div id="volcanoPlotContainer">
<canvas id="volcanoPlot"></canvas>
</div>
</div>
</div>
</div>
</div>
<!-- Enrichment Tool -->
<div id="enrichmentToolPage" class="hidden bg-white rounded-lg shadow-md p-6 space-y-6">
<div class="flex items-center">
<button id="enrichmentBackBtn" class="mr-4 p-2 rounded-full hover:bg-gray-100">
<svg class="h-6 w-6 text-gray-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg>
</button>
<h2 class="text-2xl font-bold text-gray-900">Gene Enrichment Tool</h2>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="lg:col-span-1 space-y-4">
<div class="file-upload p-8 rounded-lg text-center cursor-pointer" id="enrichmentFileUpload">
<input type="file" id="enrichmentFileInput" class="hidden" accept=".csv,.tsv,.txt">
<svg class="mx-auto h-12 w-12 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
</svg>
<h3 class="mt-2 text-sm font-medium text-gray-900">Upload your DEGs</h3>
<p class="mt-1 text-sm text-gray-500">CSV, TSV or TXT file with gene list</p>
<p class="mt-1 text-xs text-gray-500">(One gene per line or GeneID column)</p>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<h3 class="text-sm font-medium text-gray-900 mb-2">Enrichment Settings</h3>
<div class="space-y-3">
<div>
<label for="databaseSelect" class="block text-sm font-medium text-gray-700">Database</label>
<select id="databaseSelect" class="mt-1 block w-full rounded-md border-gray-300 py-2 pl-3 pr-10 text-base focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm">
<option>GO Biological Process</option>
<option>GO Molecular Function</option>
<option>GO Cellular Component</option>
<option>KEGG Pathways</option>
<option>Reactome Pathways</option>
</select>
</div>
<div>
<label for="pValueEnrichCutoff" class="block text-sm font-medium text-gray-700">p-value Cutoff</label>
<input type="number" id="pValueEnrichCutoff" value="0.05" step="0.01" min="0" max="1" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm">
</div>
<button id="runEnrichmentBtn" class="w-full bg-green-600 text-white py-2 px-4 rounded-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500">
Run Enrichment
</button>
</div>
</div>
</div>
<div class="lg:col-span-2">
<div class="bg-gray-50 p-4 rounded-lg h-full">
<h3 class="text-sm font-medium text-gray-900 mb-2">Enrichment Results</h3>
<div id="enrichmentResults" class="overflow-auto max-h-96">
<div class="text-center text-gray-500 py-8">
<svg class="mx-auto h-12 w-12 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
<p class="mt-2">Upload your DEGs and run enrichment analysis to see results</p>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
<script>
// DOM Elements
const homePage = document.getElementById('homePage');
const volcanoToolPage = document.getElementById('volcanoToolPage');
const enrichmentToolPage = document.getElementById('enrichmentToolPage');
const volcanoToolCard = document.getElementById('volcanoToolCard');
const enrichmentToolCard = document.getElementById('enrichmentToolCard');
const homeBtn = document.getElementById('homeBtn');
const volcanoBackBtn = document.getElementById('volcanoBackBtn');
const enrichmentBackBtn = document.getElementById('enrichmentBackBtn');
// File upload elements
const volcanoFileUpload = document.getElementById('volcanoFileUpload');
const volcanoFileInput = document.getElementById('volcanoFileInput');
const enrichmentFileUpload = document.getElementById('enrichmentFileUpload');
const enrichmentFileInput = document.getElementById('enrichmentFileInput');
// Buttons
const generateVolcanoBtn = document.getElementById('generateVolcanoBtn');
const runEnrichmentBtn = document.getElementById('runEnrichmentBtn');
// Results containers
const enrichmentResults = document.getElementById('enrichmentResults');
// Chart variables
let volcanoChart = null;
let enrichmentChart = null;
// Event Listeners
volcanoToolCard.addEventListener('click', () => {
homePage.classList.add('hidden');
volcanoToolPage.classList.remove('hidden');
enrichmentToolPage.classList.add('hidden');
});
enrichmentToolCard.addEventListener('click', () => {
homePage.classList.add('hidden');
enrichmentToolPage.classList.remove('hidden');
volcanoToolPage.classList.add('hidden');
});
homeBtn.addEventListener('click', () => {
homePage.classList.remove('hidden');
volcanoToolPage.classList.add('hidden');
enrichmentToolPage.classList.add('hidden');
});
volcanoBackBtn.addEventListener('click', () => {
homePage.classList.remove('hidden');
volcanoToolPage.classList.add('hidden');
});
enrichmentBackBtn.addEventListener('click', () => {
homePage.classList.remove('hidden');
enrichmentToolPage.classList.add('hidden');
});
// File upload handling
volcanoFileUpload.addEventListener('click', () => volcanoFileInput.click());
enrichmentFileUpload.addEventListener('click', () => enrichmentFileInput.click());
// Drag and drop for file uploads
[volcanoFileUpload, enrichmentFileUpload].forEach(uploadArea => {
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.classList.add('dragover');
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.classList.remove('dragover');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('dragover');
const fileInput = uploadArea === volcanoFileUpload ? volcanoFileInput : enrichmentFileInput;
if (e.dataTransfer.files.length) {
fileInput.files = e.dataTransfer.files;
updateFileUploadUI(uploadArea, e.dataTransfer.files[0].name);
}
});
});
volcanoFileInput.addEventListener('change', () => {
if (volcanoFileInput.files.length) {
updateFileUploadUI(volcanoFileUpload, volcanoFileInput.files[0].name);
}
});
enrichmentFileInput.addEventListener('change', () => {
if (enrichmentFileInput.files.length) {
updateFileUploadUI(enrichmentFileUpload, enrichmentFileInput.files[0].name);
}
});
function updateFileUploadUI(uploadArea, fileName) {
uploadArea.innerHTML = `
<svg class="mx-auto h-12 w-12 text-green-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
<h3 class="mt-2 text-sm font-medium text-gray-900">File ready</h3>
<p class="mt-1 text-sm text-gray-500">${fileName}</p>
<p class="mt-1 text-xs text-gray-500">Click to change file</p>
`;
}
// Generate Volcano Plot
generateVolcanoBtn.addEventListener('click', () => {
if (!volcanoFileInput.files.length) {
alert('Please upload a file first');
return;
}
// In a real app, you would parse the file here
// For demo purposes, we'll use mock data
const mockData = generateMockVolcanoData();
createVolcanoPlot(mockData);
});
// Run Enrichment Analysis
runEnrichmentBtn.addEventListener('click', () => {
if (!enrichmentFileInput.files.length) {
alert('Please upload a file first');
return;
}
// In a real app, you would parse the file and run enrichment
// For demo purposes, we'll use mock results
const mockResults = generateMockEnrichmentResults();
displayEnrichmentResults(mockResults);
});
// Helper functions
function generateMockVolcanoData() {
// Generate mock data for the volcano plot
const data = [];
const geneNames = ['TP53', 'BRCA1', 'EGFR', 'MYC', 'AKT1', 'PTEN', 'CDKN2A', 'RB1', 'NF1', 'APC'];
// Generate non-significant points
for (let i = 0; i < 1000; i++) {
const log2FoldChange = (Math.random() - 0.5) * 4;
const padj = Math.pow(10, -Math.random() * 5);
data.push({
symbol: i < 10 ? geneNames[i] : `Gene_${i}`,
log2FoldChange: log2FoldChange,
padj: padj
});
}
// Add significant points (more dramatic fold changes)
for (let i = 0; i < 20; i++) {
const log2FoldChange = (Math.random() > 0.5 ? 1 : -1) * (1.5 + Math.random() * 3);
const padj = Math.pow(10, -(3 + Math.random() * 4));
data.push({
symbol: `SigGene_${i}`,
log2FoldChange: log2FoldChange,
padj: padj
});
}
return data;
}
function createVolcanoPlot(data) {
const ctx = document.getElementById('volcanoPlot').getContext('2d');
const logFCCutoff = parseFloat(document.getElementById('logFCCutoff').value);
const pValueCutoff = parseFloat(document.getElementById('pValueCutoff').value);
// Prepare data for Chart.js
const plotData = {
datasets: [{
label: 'Not significant',
data: data.filter(d =>
Math.abs(d.log2FoldChange) < logFCCutoff || d.padj > pValueCutoff
).map(d => ({
x: d.log2FoldChange,
y: -Math.log10(d.padj),
symbol: d.symbol
})),
backgroundColor: 'rgba(120, 120, 120, 0.3)',
borderColor: 'rgba(120, 120, 120, 0.5)',
borderWidth: 1,
pointRadius: 3,
pointHoverRadius: 5
}, {
label: 'Up-regulated',
data: data.filter(d =>
d.log2FoldChange >= logFCCutoff && d.padj <= pValueCutoff
).map(d => ({
x: d.log2FoldChange,
y: -Math.log10(d.padj),
symbol: d.symbol
})),
backgroundColor: 'rgba(220, 50, 50, 0.8)',
borderColor: 'rgba(180, 40, 40, 1)',
borderWidth: 1,
pointRadius: 4,
pointHoverRadius: 6
}, {
label: 'Down-regulated',
data: data.filter(d =>
d.log2FoldChange <= -logFCCutoff && d.padj <= pValueCutoff
).map(d => ({
x: d.log2FoldChange,
y: -Math.log10(d.padj),
symbol: d.symbol
})),
backgroundColor: 'rgba(50, 120, 220, 0.8)',
borderColor: 'rgba(40, 90, 180, 1)',
borderWidth: 1,
pointRadius: 4,
pointHoverRadius: 6
}]
};
// Destroy previous chart if it exists
if (volcanoChart) {
volcanoChart.destroy();
}
// Create new chart
volcanoChart = new Chart(ctx, {
type: 'scatter',
data: plotData,
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
title: {
display: true,
text: 'log2 Fold Change (log2FC)',
font: {
weight: 'bold'
}
},
grid: {
color: 'rgba(0, 0, 0, 0.05)'
}
},
y: {
title: {
display: true,
text: '-log10(adj. p-value)',
font: {
weight: 'bold'
}
},
grid: {
color: 'rgba(0, 0, 0, 0.05)'
}
}
},
plugins: {
tooltip: {
callbacks: {
title: function(context) {
return context[0].raw.symbol;
},
label: function(context) {
return [
`log2FC: ${context.parsed.x.toFixed(2)}`,
`adj. p-value: ${Math.pow(10, -context.parsed.y).toExponential(2)}`
];
}
}
},
legend: {
position: 'top',
}
},
onClick: (e) => {
const points = volcanoChart.getElementsAtEventForMode(
e, 'nearest', { intersect: true }, true
);
if (points.length) {
const point = points[0];
const symbol = volcanoChart.data.datasets[point.datasetIndex].data[point.index].symbol;
alert(`Selected gene: ${symbol}`);
}
}
}
});
}
function generateMockEnrichmentResults() {
const databases = [
'GO Biological Process',
'GO Molecular Function',
'GO Cellular Component',
'KEGG Pathways',
'Reactome Pathways'
];
const selectedDB = document.getElementById('databaseSelect').value;
const results = [];
for (let i = 1; i <= 10; i++) {
results.push({
term: `${selectedDB} term ${i}`,
pValue: (Math.random() * 0.04).toFixed(4),
genes: Math.floor(Math.random() * 50) + 5,
overlap: `${Math.floor(Math.random() * 15) + 1}/${Math.floor(Math.random() * 20) + 5}`
});
}
// Sort by p-value
return results.sort((a, b) => parseFloat(a.pValue) - parseFloat(b.pValue));
}
function displayEnrichmentResults(results) {
const pValueCutoff = parseFloat(document.getElementById('pValueEnrichCutoff').value);
const filteredResults = results.filter(r => parseFloat(r.pValue) <= pValueCutoff);
// Destroy previous chart if exists
if (enrichmentChart) {
enrichmentChart.destroy();
}
if (filteredResults.length === 0) {
document.getElementById('enrichmentResults').innerHTML = `
<div class="text-center text-gray-500 py-8">
<svg class="mx-auto h-12 w-12 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<p class="mt-2">No significant enrichment results found</p>
<p class="text-sm">Try adjusting the p-value cutoff</p>
</div>
`;
return;
}
let html = `
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-100">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Term</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">p-value</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Genes</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Overlap</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
`;
filteredResults.forEach(result => {
const pValueColor = parseFloat(result.pValue) < 0.01 ? 'text-red-600' : 'text-yellow-600';
html += `
<tr class="hover:bg-gray-50 cursor-pointer gene-item" onclick="alert('${result.term}')">
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">${result.term}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm ${pValueColor}">${result.pValue}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${result.genes}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${result.overlap}</td>
</tr>
`;
});
html += `
</tbody>
</table>
</div>
<div class="mt-4 text-sm text-gray-500">
Showing ${filteredResults.length} of ${results.length} terms (p-value ≤ ${pValueCutoff})
</div>
`;
document.getElementById('enrichmentResults').innerHTML = html;
// Create enrichment chart if we have results
if (filteredResults.length > 0) {
const ctx = document.getElementById('enrichmentChart').getContext('2d');
const labels = filteredResults.map(r => r.term);
const pValues = filteredResults.map(r => -Math.log10(parseFloat(r.pValue)));
const geneCounts = filteredResults.map(r => r.genes);
// Sort by p-value (most significant first)
const sortedIndices = [...Array(filteredResults.length).keys()]
.sort((a, b) => pValues[b] - pValues[a]);
enrichmentChart = new Chart(ctx, {
type: 'bar',
data: {
labels: sortedIndices.map(i => labels[i]),
datasets: [{
label: '-log10(p-value)',
data: sortedIndices.map(i => pValues[i]),
backgroundColor: 'rgba(75, 192, 192, 0.6)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1,
yAxisID: 'y'
}, {
label: 'Gene Count',
data: sortedIndices.map(i => geneCounts[i]),
backgroundColor: 'rgba(153, 102, 255, 0.6)',
borderColor: 'rgba(153, 102, 255, 1)',
borderWidth: 1,
type: 'line',
yAxisID: 'y1'
}]
},
options: {
responsive: true,
plugins: {
title: {
display: true,
text: 'Enrichment Results',
font: {
size: 16
}
},
tooltip: {
callbacks: {
label: function(context) {
let label = context.dataset.label || '';
if (label) {
label += ': ';
}
if (context.datasetIndex === 0) {
label += context.raw.toFixed(2);
} else {
label += context.raw;
}
return label;
}
}
}
},
scales: {
y: {
type: 'linear',
display: true,
position: 'left',
title: {
display: true,
text: '-log10(p-value)'
}
},
y1: {
type: 'linear',
display: true,
position: 'right',
title: {
display: true,
text: 'Gene Count'
},
grid: {
drawOnChartArea: false
}
}
}
}
});
}
}
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Dobator/bioinformatics" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>