projects/web-mev/src/app/d3/components/gsea/gsea/gsea.component.ts
FGSEA Component
Used for GSEA analysis
| changeDetection | ChangeDetectionStrategy.Default |
| selector | mev-gsea |
| styleUrls | ./gsea.component.scss |
| templateUrl | ./gsea.component.html |
Properties |
Methods |
Inputs |
constructor(analysesService: AnalysesService, dialog: MatDialog, metadataService: MetadataService)
|
||||||||||||
|
Parameters :
|
| outputs | |
| initializeResource |
initializeResource()
|
|
Returns :
void
|
| loadPathwaysPage |
loadPathwaysPage()
|
|
Function to load pathway list by page, filter and sorting settings specified by a user
Returns :
void
|
| ngAfterViewInit |
ngAfterViewInit()
|
|
Returns :
void
|
| ngOnChanges |
ngOnChanges()
|
|
Returns :
void
|
| ngOnInit |
ngOnInit()
|
|
Returns :
void
|
| onCreateFeatureSet | ||||
onCreateFeatureSet(row)
|
||||
|
Function that is triggered when the user clicks the "Create a custom sample" button
Parameters :
Returns :
void
|
| onSubmit |
onSubmit()
|
|
Function is triggered when submitting the form with table filters
Returns :
void
|
| allowedFilters |
Type : object
|
Default value : {
padj: {
defaultValue: '',
hasOperator: true,
operatorDefaultValue: 'lte'
}
}
|
| dataSource |
Type : PathwayDataSource
|
| defaultPageIndex |
Type : number
|
Default value : 0
|
| defaultPageSize |
Type : number
|
Default value : 10
|
| defaultSorting |
Type : object
|
Default value : { field: 'pathway', direction: 'asc' }
|
| Public dialog |
Type : MatDialog
|
| displayedColumns |
Type : []
|
Default value : ['pathway', 'ranks', 'pval', 'padj', 'NES', 'actions']
|
| filterForm |
Default value : new FormGroup({})
|
| gseaResourceId |
| paginator |
Type : MatPaginator
|
Decorators :
@ViewChild(MatPaginator)
|
| sort |
Type : MatSort
|
Decorators :
@ViewChild(MatSort)
|
import {
Component,
OnInit,
ChangeDetectionStrategy,
Input,
ViewChild
} from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatDialog } from '@angular/material/dialog';
import { DataSource } from '@angular/cdk/table';
import { BehaviorSubject, Observable, merge } from 'rxjs';
import { AnalysesService } from '@app/features/analysis/services/analysis.service';
import { finalize, tap } from 'rxjs/operators';
import { FormGroup, FormControl } from '@angular/forms';
import { MetadataService } from '@app/core/metadata/metadata.service';
import { AddSampleSetComponent } from '../../dialogs/add-sample-set/add-sample-set.component';
import { CustomSetType } from '@app/_models/metadata';
/**
* FGSEA Component
*
* Used for GSEA analysis
*/
@Component({
selector: 'mev-gsea',
templateUrl: './gsea.component.html',
styleUrls: ['./gsea.component.scss'],
changeDetection: ChangeDetectionStrategy.Default
})
export class GseaComponent implements OnInit {
@Input() outputs;
dataSource: PathwayDataSource; // datasource for MatTable
gseaResourceId;
@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
/* Table settings */
displayedColumns = ['pathway', 'ranks', 'pval', 'padj', 'NES', 'actions'];
operators = [
{ id: 'eq', name: ' = ' },
{ id: 'gte', name: ' >=' },
{ id: 'gt', name: ' > ' },
{ id: 'lte', name: ' <=' },
{ id: 'lt', name: ' < ' },
{ id: 'absgt', name: 'ABS(x) > ' },
{ id: 'abslt', name: 'ABS(x) < ' }
];
defaultPageIndex = 0;
defaultPageSize = 10;
defaultSorting = { field: 'pathway', direction: 'asc' };
/* Table filters */
allowedFilters = {
padj: {
defaultValue: '',
hasOperator: true,
operatorDefaultValue: 'lte'
}
};
filterForm = new FormGroup({});
constructor(
private analysesService: AnalysesService,
public dialog: MatDialog,
private metadataService: MetadataService
) {
this.dataSource = new PathwayDataSource(this.analysesService);
// adding form controls depending on the tables settings (the allowedFilters property)
for (const key in this.allowedFilters) {
if (this.allowedFilters.hasOwnProperty(key)) {
// TSLint rule
const defaultValue = this.allowedFilters[key].defaultValue;
this.filterForm.addControl(key, new FormControl(defaultValue));
if (this.allowedFilters[key].hasOperator) {
const operatorDefaultValue = this.allowedFilters[key]
.operatorDefaultValue;
this.filterForm.addControl(
key + '_operator',
new FormControl(operatorDefaultValue)
);
}
}
}
}
ngOnInit() {
this.initializeResource();
}
ngAfterViewInit() {
this.sort.sortChange.subscribe(
() => (this.paginator.pageIndex = this.defaultPageIndex)
);
this.dataSource.connect().subscribe(() => {});
merge(this.sort.sortChange, this.paginator.page)
.pipe(
tap(() => {
this.loadPathwaysPage();
})
)
.subscribe();
}
ngOnChanges(): void {
this.initializeResource();
}
initializeResource(): void {
this.gseaResourceId = this.outputs.pathway_results;
const sorting = {
sortField: this.defaultSorting.field,
sortDirection: this.defaultSorting.direction
};
this.dataSource.loadPathways(
this.gseaResourceId,
{},
sorting,
this.defaultPageIndex,
this.defaultPageSize
);
}
/**
* Function is triggered when submitting the form with table filters
*/
onSubmit() {
this.paginator.pageIndex = this.defaultPageIndex;
this.loadPathwaysPage();
}
/**
* Function that is triggered when the user clicks the "Create a custom sample" button
*/
onCreateFeatureSet(row) {
const features = row.leadingEdge.map(elem => ({
id: elem
}));
const dialogRef = this.dialog.open(AddSampleSetComponent, {
data: { type: CustomSetType.FeatureSet, name: row.pathway }
});
dialogRef.afterClosed().subscribe(customSetData => {
if (customSetData) {
const customSet = {
name: customSetData.name,
type: CustomSetType.FeatureSet,
elements: features,
multiple: true
};
this.metadataService.addCustomSet(customSet);
}
});
}
/**
* Function to load pathway list by page, filter and sorting settings specified by a user
*/
loadPathwaysPage() {
const formValues = this.filterForm.value; // i.e. {name: "asdfgh", pvalue: 3, pvalue_operator: "lte", log2FoldChange: 2, log2FoldChange_operator: "lte"}
const paramFilter = {}; // has values {'log2FoldChange': '[absgt]:2'};
for (const key in this.allowedFilters) {
if (
formValues.hasOwnProperty(key) &&
formValues[key] !== '' &&
formValues[key] !== null
) {
if (formValues.hasOwnProperty(key + '_operator')) {
paramFilter[key] =
'[' + formValues[key + '_operator'] + ']:' + formValues[key];
} else {
paramFilter[key] = '[eq]:' + formValues[key];
}
}
}
const sorting = {
sortField: this.sort.active,
sortDirection: this.sort.direction
};
this.dataSource.loadPathways(
this.gseaResourceId,
paramFilter,
sorting,
this.paginator.pageIndex,
this.paginator.pageSize
);
}
}
export interface Pathway {
pathway: string;
pval: number;
padj: number;
log2err: number;
ES: number;
NES: number;
size: number;
ranks: number[];
}
export class PathwayDataSource implements DataSource<Pathway> {
public pathwaysSubject = new BehaviorSubject<Pathway[]>([]);
public pathwaysCount = 0;
private loadingSubject = new BehaviorSubject<boolean>(false);
public loading$ = this.loadingSubject.asObservable();
constructor(private analysesService: AnalysesService) {}
loadPathways(
resourceId: string,
filterValues: object,
sorting: object,
pageIndex: number,
pageSize: number
) {
this.loadingSubject.next(true);
this.analysesService
.getResourceContent(
resourceId,
pageIndex + 1,
pageSize,
filterValues,
sorting
)
.pipe(finalize(() => this.loadingSubject.next(false)))
.subscribe(response => {
this.pathwaysCount = response.count;
return this.pathwaysSubject.next(response.results);
});
}
connect(): Observable<Pathway[]> {
return this.pathwaysSubject.asObservable();
}
disconnect(): void {
this.pathwaysSubject.complete();
this.loadingSubject.complete();
}
}
<mat-card class="analysis-card">
<mat-card-header>
<div mat-card-avatar class="analysis-card__img"></div>
<mat-card-title>FGSEA: {{ outputs?.job_name }}</mat-card-title>
</mat-card-header>
<mat-card-content class="analysis-card__main">
<p class="analysis-card__instruction">
Use the table pagination component at the bottom of the table to navigate
between a set of table and chart results. <br>
You can sort data in alphabetical and numerical order,
or use filters to hide data you don't want to see.
</p>
<mat-divider [inset]="true"></mat-divider>
<div class="analysis-card__content">
<section class="filter-section">
<form [formGroup]="filterForm" (ngSubmit)="onSubmit()">
<div class="filter">
<span class="label">
P-value adjusted:
</span>
<mat-form-field class="form-control form-control__small" color="accent">
<mat-select formControlName="padj_operator">
<mat-option *ngFor="let operator of operators" [value]="operator.id">
{{ operator.name }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="form-control">
<input matInput formControlName="padj" type=number step=any min=0>
</mat-form-field>
</div>
<button type="submit" [disabled]="!filterForm.valid" mat-raised-button color="accent">Apply filters</button>
</form>
</section>
<div class="mat-elevation-z8">
<mat-table class="deseq-table" [dataSource]="dataSource" matSort [matSortActive]="defaultSorting.field"
[matSortDirection]="defaultSorting.direction" matSortDisableClear>
<ng-container matColumnDef="pathway">
<mat-header-cell *matHeaderCellDef>Pathway</mat-header-cell>
<mat-cell *matCellDef="let row">{{row.pathway}}</mat-cell>
</ng-container>
<ng-container matColumnDef="NES">
<mat-header-cell *matHeaderCellDef>NES</mat-header-cell>
<mat-cell class="description-cell" *matCellDef="let row">{{row.NES}}</mat-cell>
</ng-container>
<ng-container matColumnDef="padj">
<mat-header-cell *matHeaderCellDef mat-sort-header>P-value adjusted</mat-header-cell>
<mat-cell class="description-cell" *matCellDef="let row">{{row.padj}}</mat-cell>
</ng-container>
<ng-container matColumnDef="pval">
<mat-header-cell *matHeaderCellDef mat-sort-header>P-value</mat-header-cell>
<mat-cell class="description-cell" *matCellDef="let row">{{row.pval}}</mat-cell>
</ng-container>
<ng-container matColumnDef="ranks">
<mat-header-cell *matHeaderCellDef mat-sort-header>Gene ranks</mat-header-cell>
<mat-cell class="description-cell" *matCellDef="let row">
<mev-rug-plot [plotData]="row.ranks"></mev-rug-plot>
</mat-cell>
</ng-container>
<ng-container matColumnDef="actions">
<mat-header-cell *matHeaderCellDef></mat-header-cell>
<mat-cell *matCellDef="let row; let i=index;">
<button mat-raised-button color="accent" title="Create feature set"
(click)="onCreateFeatureSet(row)">
Create feature set
</button>
<!-- <button mat-raised-button color="accent" title="Show top genes"
(click)="onShowTopGenes(row)">
Show top genes
</button> -->
</mat-cell>
</ng-container>
<!-- <ng-container matColumnDef="topGeneView">
<mat-header-cell *matHeaderCellDef class="file-table-header"></mat-header-cell>
<mat-cell *matCellDef="let row; let i=index;">
</mat-cell>
</ng-container> -->
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
</mat-table>
<mat-paginator [length]="dataSource.pathwaysCount" [pageSize]="10" [pageSizeOptions]="[10, 25, 50]">
</mat-paginator>
</div>
<!-- <div class="button-panel">
<button mat-raised-button color="accent" (click)="onCreateCustomFeatureSet()">
<mat-icon>add</mat-icon>
Save as a feature set
</button>
<mev-download-button [containerId]="containerId" [imageName]="imageName"></mev-download-button>
</div> -->
</div>
</mat-card-content>
</mat-card>
./gsea.component.scss
.analysis-card__img {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' aria-hidden='true' focusable='false' width='1em' height='1em' style='-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);' preserveAspectRatio='xMidYMid meet' viewBox='0 0 24 24'%3E%3Cpath d='M4 2h2v2c0 1.44.68 2.61 1.88 3.78c.86.83 2.01 1.63 3.21 2.42l-1.83 1.19C8.27 10.72 7.31 10 6.5 9.21C5.07 7.82 4 6.1 4 4V2m14 0h2v2c0 2.1-1.07 3.82-2.5 5.21c-1.41 1.38-3.21 2.52-4.96 3.63c-1.75 1.12-3.45 2.21-4.66 3.38C6.68 17.39 6 18.56 6 20v2H4v-2c0-2.1 1.07-3.82 2.5-5.21c1.41-1.38 3.21-2.52 4.96-3.63c1.75-1.12 3.45-2.21 4.66-3.38C17.32 6.61 18 5.44 18 4V2m-3.26 10.61c.99.67 1.95 1.39 2.76 2.18C18.93 16.18 20 17.9 20 20v2h-2v-2c0-1.44-.68-2.61-1.88-3.78c-.86-.83-2.01-1.63-3.21-2.42l1.83-1.19M7 3h10v1l-.06.5H7.06L7 4V3m.68 3h8.64c-.24.34-.52.69-.9 1.06l-.51.44H9.07l-.49-.44c-.38-.37-.66-.72-.9-1.06m1.41 10.5h5.84l.49.44c.38.37.66.72.9 1.06H7.68c.24-.34.52-.69.9-1.06l.51-.44m-2.03 3h9.88l.06.5v1H7v-1l.06-.5z' fill='%2337474f'/%3E%3C/svg%3E");
background-size: 30px;
background-position: top center;
background-repeat: no-repeat;
}
.analysis-card__content {
padding-top: 1rem;
}
.filter-section {
font-size: 14px;
}
.filter {
margin-right: 25px;
display: inline;
}
.form-control {
margin-right: 10px;
}
.form-control__small {
width: 80px;
}
.button-panel {
margin-top: 30px;
}
.mat-column-pathway,
.mat-column-ranks {
word-wrap: break-word !important;
white-space: unset !important;
flex: 0 0 20% !important;
width: 20% !important;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-word;
-ms-hyphens: auto;
-moz-hyphens: auto;
-webkit-hyphens: auto;
hyphens: auto;
}
.mat-column-actions {
flex-direction: row;
flex-wrap: wrap;
button {
margin: 5px;
}
}
mev-rug-plot {
width: 100% !important;
}