Skip to content

Commit

Permalink
Add ase track
Browse files Browse the repository at this point in the history
  • Loading branch information
e-sollier committed Oct 31, 2024
1 parent 1ae7a8a commit 2d11583
Show file tree
Hide file tree
Showing 13 changed files with 233 additions and 30 deletions.
26 changes: 26 additions & 0 deletions docs/content/describe_figure.rst
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,32 @@ Parameters:

* ``grid_cn``: whether or not to display horizontal lines for each integer copy number.

ase
^^^^^^^^

Allele specific expression: track showing the variant allele frequency of SNPs in RNA-seq and DNA-seq data. This can be useful to show when only one allele is expressed, for example for imprinted genes or genes activated by enhancer hijacking.
The input data required for this track is a tsv file which can be generated by `fast_ase <https://github.com/e-sollier/fast_ase>`_ and which has the following columns: contig, position, variantID, refAllele, altAllele, refCount, altCount, refCount_DNA, and altCount_DNA (where refCount and altCount are the counts of reads supporting the reference and alternative alleles in the RNA-seq data).

.. image:: images/figure_ase.png
:width: 500

Parameters:

* ``file``: Path to the tsv file generated by `fast_ase <https://github.com/e-sollier/fast_ase>`_.

* ``vcf_DNA``: Path to a vcf file containing the read counts in the DNA-seq data. This is only required if, instead of using fast_ase, you used GATK HaplotypeCaller and GATK ASEReadCounter for preprocessing the bam files.

* ``color1``, ``color2``: Colors for the minor and major allele frequencies.

* ``min_depth``: Minimum number of reads covering a variant in the RNA-seq data to show it.

* ``max_bar_width``: Maximum width of the bars, in mm. If too many variants are shown, the actual width might be narrower, to avoid overlaps.

* ``lw``: Line width for the line connecting each bar to the position of the corresponding SNP.

* ``only_exonic``: If true, will only show variants located in exons (default: false).

* ``grid``: if true, will display horizontal lines at VAFs of 0.25, 0.50 and 0.75 (default: false).



Expand Down
84 changes: 84 additions & 0 deletions docs/content/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1143,4 +1143,88 @@ Here is an example of nanopore reads spanning a foldback inversion (resulting fr
"chr_prefix": "chr"
}
]
}

Allele-specific expression
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The ase track can be used to visualize allele-specific expression. For each variant, the minor allele frequency in the DNA is shown at the top (showing that the variants are heterozygous), and the minor allele frequency in the RNA-seq data is shown at the bottom.
Here, we see that only one allele of L3MBTL1 is expressed, because this gene is imprinted.

.. image:: images/figure_ase.png
:width: 500

.. toggle::

.. code:: json
{
"general": {
"layout": "horizontal",
"reference": "hg19"
},
"output": {
"file": "/home/e840r/Documents/Scripts/figeno/figeno/gui/figure_ase.png",
"dpi": 400,
"width": 100
},
"regions": [
{
"chr": "20",
"start": 42134174,
"end": 42181756
}
],
"highlights": [],
"tracks": [
{
"type": "ase",
"height": 50,
"margin_above": 1.5,
"bounding_box": false,
"fontscale": 1,
"label": "RNA,DNA",
"label_rotate": true,
"file": "/path/to/sample.tsv",
"vcf_DNA": "",
"min_depth": "6",
"color1": "#e55039",
"color2": "#4a69bd",
"max_bar_width": "10.0",
"lw": "0.1",
"only_exonic": false,
"grid": false
},
{
"type": "genes",
"height": 10,
"margin_above": 1.5,
"bounding_box": false,
"fontscale": 1,
"label": "",
"label_rotate": false,
"style": "default",
"collapsed": true,
"only_protein_coding": true,
"exon_color": "#2980b9",
"genes": "auto",
"show_gene_names": true
},
{
"type": "chr_axis",
"height": 10,
"margin_above": 1.5,
"bounding_box": false,
"fontscale": 1,
"label": "",
"label_rotate": false,
"style": "default",
"lw_scale": "1.0",
"ticklabels_pos": "below",
"unit": "kb",
"ticks_interval": "auto",
"ticks_angle": 0,
"chr_prefix": "chr"
}
]
}
Binary file added docs/content/images/figure_ase.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 11 additions & 3 deletions figeno/ase.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import numpy as np
import pandas as pd
import vcfpy
from figeno.genes import Gene
from figeno.utils import KnownException


def read_SNPs_RNA(ase_file,gene=None,chr=None,start=None,end=None,exons=None,min_depth=0):
"""
Reads a tsv file generated by ASEReadCounter.
Reads a tsv file generated by fast_ase or ASEReadCounter.
Return a dataframe whose rows are SNPs, indicating the read counts of each allele in the RNAseq data.
"""
if gene is None and (chr is None or start is None or end is None):
Expand All @@ -15,7 +15,15 @@ def read_SNPs_RNA(ase_file,gene=None,chr=None,start=None,end=None,exons=None,min
chr = gene.chr
start = gene.start
end = gene.end
df = pd.read_csv(ase_file,sep="\t",dtype={"contig":str})
try:
df = pd.read_csv(ase_file,sep="\t",dtype={"contig":str})
except:
raise KnownException("Failed to open the ase file: "+str(ase_file))
if not "contig" in df.columns: raise KnownException("The input file for the ase track needs a contig column.")
if not "position" in df.columns: raise KnownException("The input file for the ase track needs a position column.")
if not "refCount" in df.columns: raise KnownException("The input file for the ase track needs a refCount column.")
if not "altCount" in df.columns: raise KnownException("The input file for the ase track needs a altCount column.")
if not "variantID" in df.columns: raise KnownException("The input file for the ase track needs a variantID column.")
df["contig"] = [x.lstrip("chr") for x in df["contig"]]
df = df.loc[df["contig"]==chr]
df = df.loc[(df["position"]>=start) & (df["position"]<=end)]
Expand Down
2 changes: 1 addition & 1 deletion figeno/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from figeno.cli import gui, init,make

__version__ = "1.5.1"
__version__ = "1.6.0"

def main():
parser = ArgumentParser("figeno",formatter_class=ArgumentDefaultsHelpFormatter)
Expand Down
10 changes: 5 additions & 5 deletions figeno/gui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion figeno/gui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "figeno",
"version": "1.5.1",
"version": "1.6.0",
"private": true,
"homepage": "./",
"dependencies": {
Expand Down
59 changes: 59 additions & 0 deletions figeno/gui/src/Track.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ export function Track({track, set_value, change_track_type, copy_track, delete_t
if (track.type=="copynumber"){
return CopynumberTrack({"track":track,"set_value":set_value,"openColorPanel":openColorPanel,"setFileDialogData":setFileDialogData,"setFileDialogActive":setFileDialogActive});
}
if (track.type=="ase"){
return AseTrack({"track":track,"set_value":set_value,"openColorPanel":openColorPanel,"setFileDialogData":setFileDialogData,"setFileDialogActive":setFileDialogActive});
}
}
function handleTrackIcon(){
openTrackTypePanel((t)=>{set_value("type",t)})
Expand Down Expand Up @@ -784,6 +787,49 @@ function CopynumberTrack({track,set_value,openColorPanel, setFileDialogData,setF
)
}

function AseTrack({track,set_value,openColorPanel, setFileDialogData,setFileDialogActive}){
const fileClass= (track.file.endsWith(".tsv"))? "":"unsetPath";
return (
<>
<div className="optionGroup">
<PathEntry id={"file"+track.id} label="File:" value={track.file} set_value={(val) => set_value("file",val)} className={fileClass} setFileDialogData={setFileDialogData} setFileDialogActive={setFileDialogActive}/>
<PathEntry id={"vcf_DNA"+track.id} label="VCF DNA:" value={track.vcf_DNA} set_value={(val) => set_value("vcf_DNA",val)} setFileDialogData={setFileDialogData} setFileDialogActive={setFileDialogActive}/>
<div className='formItem' >
<label htmlFor={"color1"+track.id}>Color 1: </label>
<ColorButton id={"color1"+track.id} color={track.color1} setColor={(c)=>set_value("color1",c)} openColorPanel={openColorPanel}></ColorButton>
</div>
<div className='formItem' >
<label htmlFor={"color2"+track.id}>Color 2: </label>
<ColorButton id={"color2"+track.id} color={track.color2} setColor={(c)=>set_value("color2",c)} openColorPanel={openColorPanel}></ColorButton>
</div>
</div>
<div className="optionGroup">
<div className='formItem'>
<label htmlFor={"min_depth"+track.id}>Min depth:</label>
<input id={"min_depth"+track.id} style={{width:"3em"}} value={track.min_depth} onChange={(e) => set_value("min_depth",e.target.value)} ></input>
</div>
<div className='formItem'>
<label htmlFor={"max_bar_width"+track.id}>Max bar width:</label>
<input id={"max_bar_width"+track.id} style={{width:"3em"}} value={track.max_bar_width} onChange={(e) => set_value("max_bar_width",e.target.value)} ></input>
</div>
<div className='formItem'>
<label htmlFor={"lw"+track.id}>lw:</label>
<input id={"lw"+track.id} style={{width:"3em"}} value={track.lw} onChange={(e) => set_value("lw",e.target.value)} ></input>
</div>
<div className='formItem'>
<label htmlFor={"only_exonic"+track.id}>Only exonic:</label>
<input type="checkbox" id={"only_exonic"+track.id} checked={track.only_exonic} onChange={() => set_value("only_exonic",!track.only_exonic)} />
</div>
<div className='formItem'>
<label htmlFor={"grid"+track.id}>Grid:</label>
<input type="checkbox" id={"grid"+track.id} checked={track.grid} onChange={() => set_value("grid",!track.grid)} />
</div>

</div>

</>
)
}

export function TrackIcon({track_type, onClick}){
return(
Expand Down Expand Up @@ -887,6 +933,19 @@ export function TrackIcon({track_type, onClick}){
</g>
</svg>
):""}

{(track_type=="ase")?(
<svg width="15mm" height="15mm" version="1.1" viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">
<g stroke-linejoin="round" strokeLinecap="round">
<rect x="2.8333" y="3" width="1.6667" height="1.6667" fill="#e55039" opacity=".97208" stroke-width=".1093"/>
<rect x="2.8333" y="1.3333" width="1.6667" height="1.6667" fill="#4a69bd" opacity=".97208" stroke-width=".1093"/>
<rect x="2.8333" y="5.3333" width="1.6667" height="3.3333" fill="#4a69bd" opacity=".97208" stroke-width=".15458"/>
<rect x="5.5" y="3.2029" width="1.6667" height="1.4637" fill="#e55039" opacity=".97208" stroke-width=".10243"/>
<rect x="5.5" y="1.3333" width="1.6667" height="1.8887" fill="#4a69bd" opacity=".97208" stroke-width=".11635"/>
<rect x="5.5" y="5.3333" width="1.6667" height="3.3333" fill="#4a69bd" opacity=".97208" stroke-width=".15458"/>
</g>
</svg>
):""}
</div>


Expand Down
2 changes: 1 addition & 1 deletion figeno/gui/src/TrackTypePanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { TrackIcon } from './Track';


export function TrackTypePanel({setTrackType,close}){
const track_types = ["chr_axis", "genes", "bed", "bigwig", "coverage", "alignments", "basemod_freq", "hic", "sv", "copynumber"];
const track_types = ["chr_axis", "genes", "bed", "bigwig", "coverage", "alignments", "basemod_freq", "hic", "sv", "copynumber", "ase"];
const popover = useRef();
useClickOutside(popover, close);

Expand Down
19 changes: 18 additions & 1 deletion figeno/gui/src/TracksContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,23 @@ export const defaultTrackValues={
color_gain:"#e55039",
color_cnloh:"#f6b93b"
},
"ase":{
height:"50",
margin_above:"1.5",
bounding_box:false,
fontscale:1.0,
label:"RNA,DNA",
label_rotate:true,
file:"",
vcf_DNA:"",
min_depth:"6",
color1:"#e55039",
color2:"#4a69bd",
max_bar_width:"10.0",
lw: "0.1",
only_exonic: false,
grid:false
},
"other":{
height:"8",
margin_above:"1.5",
Expand Down Expand Up @@ -227,7 +244,7 @@ export function TracksContainer({tracksList,setTracksList,openColorPanel, openTr

function create_track({id,type,track={}}){
let new_track = {id:id, type:type};
if (["chr_axis","genes","bed","bigwig","coverage","alignments", "basemod_freq","hic","sv","copynumber"].includes(new_track.type)){
if (["chr_axis","genes","bed","bigwig","coverage","alignments", "basemod_freq","hic","sv","copynumber","ase"].includes(new_track.type)){
for (const attribute in defaultTrackValues[new_track.type]){
new_track[attribute] = track.hasOwnProperty(attribute) ? track[attribute] : defaultTrackValues[new_track.type][attribute]
}
Expand Down
1 change: 1 addition & 0 deletions figeno/gui/src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ input{
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap:10px;
margin:5px;
}

.TemplatePanel{
Expand Down
Loading

0 comments on commit 2d11583

Please sign in to comment.