Skip to content

Commit 0448fb8

Browse files
committed
Limits and labels for plots.
1 parent 40d2fca commit 0448fb8

File tree

6 files changed

+124
-29
lines changed

6 files changed

+124
-29
lines changed

Diff for: NetExplorer/models.py

+45-12
Original file line numberDiff line numberDiff line change
@@ -1750,6 +1750,8 @@ class ScatterPlot(object):
17501750
'''
17511751
def __init__(self):
17521752
self.traces = dict()
1753+
self.limits = { 'x': list(), 'y': list()}
1754+
self.units = dict()
17531755

17541756
def add_trace(self, name):
17551757
if name not in self.traces:
@@ -1770,6 +1772,12 @@ def add_name(self, trace_name, name):
17701772
if trace_name in self.traces:
17711773
self.traces[trace_name].names.append(name)
17721774

1775+
def set_limits(self, axis, start, end):
1776+
if axis == 'x' or axis == 'y':
1777+
self.limits[axis] =[start, end]
1778+
else:
1779+
raise ValueError("Axis should be 'x' or 'y'.")
1780+
17731781
def plot(self):
17741782
theplot = dict()
17751783
theplot['data'] = list()
@@ -1790,6 +1798,16 @@ def plot(self):
17901798
if trace.names:
17911799
trace_data['text'] = trace.names
17921800
theplot['data'].append(trace_data)
1801+
theplot['layout'] = {'xaxis': dict(), 'yaxis': dict()}
1802+
if self.limits:
1803+
if 'x' in self.limits:
1804+
theplot['layout']['xaxis']['range'] = self.limits['x']
1805+
if 'y' in self.limits:
1806+
theplot['layout']['yaxis']['range'] = self.limits['y']
1807+
if self.units:
1808+
for axis, units in self.units.items():
1809+
axis_name = axis + 'axis'
1810+
theplot['layout'][axis_name]['title'] = units
17931811
return theplot
17941812

17951813
def add_color_to_trace(self, trace_name, colors):
@@ -1798,6 +1816,21 @@ def add_color_to_trace(self, trace_name, colors):
17981816
else:
17991817
raise(KeyError("Trace %s not found in ScatterPlot!" % trace_name))
18001818

1819+
def add_units(self, axis, units):
1820+
'''
1821+
Adds units to one axis of the plot.
1822+
1823+
Args:
1824+
axis: string cointaining 'x' or 'y'.
1825+
units: string for units.
1826+
1827+
Returns:
1828+
nothing
1829+
'''
1830+
if axis == 'x' or axis == 'y':
1831+
self.units[axis] = units
1832+
else:
1833+
raise ValueError("Axis should be a string containing 'x' or 'y'.")
18011834

18021835

18031836
class PlotlyTrace(object):
@@ -1947,7 +1980,7 @@ def get_allowed_datasets(cls, user):
19471980
all_allowed = public_datasets | restricted_allowed
19481981
return all_allowed
19491982

1950-
def __unicode__(self):
1983+
def __str__(self):
19511984
return self.name
19521985

19531986

@@ -1956,7 +1989,7 @@ class UserDatasetPermission(models.Model):
19561989
user = models.ForeignKey(User, on_delete=models.CASCADE)
19571990
dataset = models.ForeignKey(Dataset, on_delete=models.CASCADE)
19581991

1959-
def __unicode__(self):
1992+
def __str__(self):
19601993
return self.user.username + " [access to] " + self.dataset.name
19611994

19621995

@@ -1965,7 +1998,7 @@ class ExperimentType(models.Model):
19651998
exp_type = models.CharField(max_length=50)
19661999
description = models.TextField()
19672000

1968-
def __unicode__(self):
2001+
def __str__(self):
19692002
return self.exp_type
19702003

19712004

@@ -2015,15 +2048,15 @@ def to_json(self):
20152048
json_string = json.dumps(json_dict)
20162049
return json_string
20172050

2018-
def __unicode__(self):
2051+
def __str__(self):
20192052
return self.name
20202053

20212054
# ------------------------------------------------------------------------------
20222055
class ExperimentDataset(models.Model):
20232056
dataset = models.ForeignKey(Dataset, on_delete=models.CASCADE)
20242057
experiment = models.ForeignKey(Experiment, on_delete=models.CASCADE)
20252058

2026-
def __unicode__(self):
2059+
def __str__(self):
20272060
return self.experiment.name + ' - ' + self.dataset.name
20282061

20292062

@@ -2032,7 +2065,7 @@ class UserExperimentPermission(models.Model):
20322065
user = models.ForeignKey(User, on_delete=models.CASCADE)
20332066
experiment = models.ForeignKey(Experiment, on_delete=models.CASCADE)
20342067

2035-
def __unicode__(self):
2068+
def __str__(self):
20362069
return self.user.username + " [access to] " + self.experiment.name
20372070

20382071

@@ -2046,7 +2079,7 @@ class ConditionType(models.Model):
20462079
name = models.CharField(max_length=50)
20472080
description = models.TextField()
20482081

2049-
def __unicode__(self):
2082+
def __str__(self):
20502083
return self.name
20512084

20522085

@@ -2062,7 +2095,7 @@ class Condition(models.Model):
20622095
cell_type = models.CharField(max_length=50)
20632096
description = models.TextField()
20642097

2065-
def __unicode__(self):
2098+
def __str__(self):
20662099
return self.name + " - " + self.experiment.name
20672100

20682101

@@ -2071,7 +2104,7 @@ class Sample(models.Model):
20712104
experiment = models.ForeignKey(Experiment, on_delete=models.CASCADE)
20722105
sample_name = models.CharField(max_length=50)
20732106

2074-
def __unicode__(self):
2107+
def __str__(self):
20752108
return self.sample_name + " - " + self.experiment.name
20762109

20772110

@@ -2081,7 +2114,7 @@ class SampleCondition(models.Model):
20812114
sample = models.ForeignKey(Sample, on_delete=models.CASCADE)
20822115
condition = models.ForeignKey(Condition, on_delete=models.CASCADE)
20832116

2084-
def __unicode__(self):
2117+
def __str__(self):
20852118
return self.experiment.name + " - " + self.sample.sample_name + " - " + self.condition.name
20862119

20872120
# ------------------------------------------------------------------------------
@@ -2100,7 +2133,7 @@ class ExpressionAbsolute(models.Model):
21002133
expression_value = models.FloatField()
21012134
units = models.CharField(max_length=10)
21022135

2103-
def __unicode__(self):
2136+
def __str__(self):
21042137
name_str = self.experiment.name + " - "
21052138
name_str += str(self.sample.sample_name) + " - "
21062139
name_str += str(self.gene_symbol) + ": "
@@ -2128,7 +2161,7 @@ class ExpressionRelative(models.Model):
21282161
fold_change = models.FloatField()
21292162
pvalue = models.FloatField()
21302163

2131-
def __unicode__(self):
2164+
def __str__(self):
21322165
name_str = self.experiment.name + " - "
21332166
name_str += self.condition1.name + " vs "
21342167
name_str += self.condition2.name + " - "

Diff for: NetExplorer/static/css/style.css

+7
Original file line numberDiff line numberDiff line change
@@ -2022,6 +2022,13 @@ kbd {
20222022
background-color: #f4f4f4;
20232023
}
20242024

2025+
#volcano-plot {
2026+
display: inline-block;
2027+
height: 500px;
2028+
min-width: 700px;
2029+
background-color: #f4f4f4;
2030+
}
2031+
20252032
#plot-genenotfound {
20262033
display: none;
20272034
margin: auto;

Diff for: NetExplorer/static/js/planexp.js

+12-9
Original file line numberDiff line numberDiff line change
@@ -144,16 +144,14 @@ var PlanExp = (function() {
144144
}
145145

146146
/**
147-
* experimentDGETable
147+
* getDatasets
148148
* Summary:
149-
* Performs AJAX query to /experiment_dge_table,
150-
* retrieving the HTML code of the differential
151-
* expression table and puting it in targetDiv.
149+
*
150+
*
152151
* Arguments:
153-
* - Experiment name string
154-
* - jQuery object of div to put the table.
152+
*
155153
* Returns:
156-
* - Nothing
154+
*
157155
*/
158156
getDatasets = function(expName, datasetSelect) {
159157
datasetRow = function(datasetName) {
@@ -214,9 +212,10 @@ var PlanExp = (function() {
214212
$("#dge-table-notfound").show(250);
215213
} else {
216214
$("#dge-table-notfound").hide();
217-
targetDiv.html(data);
215+
targetDiv.html(data.table);
218216
targetDiv.hide();
219217
targetDiv.show(250);
218+
Plotly.newPlot("volcano-plot", data.volcano);
220219
}
221220
}
222221
});
@@ -341,6 +340,8 @@ var PlanExp = (function() {
341340
$('#planexp-dge-c1').selectpicker('val', '');
342341
$('#planexp-dge-c2').selectpicker('val', '');
343342
$("#expression-plot").html("");
343+
$("#plot-tsne").html("");
344+
$("#volcano-plot").html("");
344345
$("#plot-genenotfound").hide();
345346

346347
// Change DGE table ConditionType select
@@ -377,7 +378,9 @@ var PlanExp = (function() {
377378
if ( ! condition1 || ! condition2 ) {
378379
return;
379380
}
380-
381+
382+
// Clean volcano plot
383+
$("#volcano-plot").html("");
381384
experimentDGETable(expName, dataset, condition1, condition2, $("#planexp-dge-table"));
382385

383386
});

Diff for: NetExplorer/templates/NetExplorer/planexp.html

+5
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ <h4>Select conditions to compare</h4>
101101
</div>
102102
<div id="planexp-dge-table">
103103
</div>
104+
105+
<div id="plot-container">
106+
<div id="volcano-plot">
107+
</div>
108+
</div>
104109
</div> <!-- Differential expression table -->
105110

106111
<!-- Gene Expression Plot-->

Diff for: NetExplorer/views/common.py

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from django.http import HttpResponse
55
from django.template import RequestContext
66
from NetExplorer.models import *
7+
from django.db.models import Func, F
78
from subprocess import Popen, PIPE, STDOUT
89
from django.contrib.staticfiles.templatetags.staticfiles import static
910
from django.contrib.auth.models import User

Diff for: NetExplorer/views/experiment_dge_table.py

+54-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,49 @@
11
from .common import *
22

3+
4+
def do_volcano_plot(expression):
5+
'''
6+
Creates a Volcano plot with a given comparison of conditions.
7+
'''
8+
theplot = ScatterPlot()
9+
trace_name = "Volcano Plot"
10+
theplot.add_trace(trace_name)
11+
max_x = 0
12+
min_x = 0
13+
max_y = 0
14+
for gexp in expression:
15+
x = gexp.fold_change
16+
y = -(math.log10(gexp.pvalue))
17+
theplot.add_x(trace_name, x)
18+
theplot.add_y(trace_name, y)
19+
theplot.add_name(trace_name, gexp.gene_symbol)
20+
21+
# Get max and min X
22+
if min_x == 0:
23+
min_x = x
24+
if x > max_x:
25+
max_x = x
26+
elif x < min_x:
27+
min_x = x
28+
29+
# Get max and min Y
30+
if y > max_y:
31+
max_y = y
32+
33+
bigger_x = max([abs(min_x), abs(max_x)]) + 1
34+
theplot.set_limits('x', -bigger_x, bigger_x)
35+
theplot.set_limits('y', 0, max_y + max_y * 0.10) # Add +10% to make plot axis more visible
36+
theplot.add_units('x', 'log10 ( Fold Change ) ')
37+
theplot.add_units('y', '-log10 ( pvalue ) ')
38+
return theplot
39+
40+
341
def experiment_dge_table(request):
442
"""
543
View from PlanExp that returns the HTML of a table comparing two conditions
644
"""
45+
max_genes = 250
46+
pvalue_threshold = 0.001
747
if request.is_ajax():
848
exp_name = request.GET['experiment']
949
c1_name = request.GET['condition1']
@@ -15,17 +55,23 @@ def experiment_dge_table(request):
1555
condition2 = Condition.objects.get(name=c2_name, experiment=experiment)
1656
expression = ExpressionRelative.objects.filter(
1757
experiment=experiment, dataset=dataset,
18-
condition1=condition1, condition2=condition2).order_by('-fold_change')[:500]
58+
condition1=condition1, condition2=condition2, pvalue__lte=pvalue_threshold)
1959
if not expression:
2060
# In case condition1 and condition2 are reversed in Database
2161
expression = ExpressionRelative.objects.filter(
2262
experiment=experiment, dataset=dataset,
23-
condition1=condition2, condition2=condition1).order_by('-fold_change')[:500]
24-
if not expression:
25-
response = {}
26-
return HttpResponse(json.dumps(response), content_type="application/json")
27-
else:
28-
response = { 'expressions' : expression, 'database': dataset }
29-
return render(request, 'NetExplorer/experiment_dge_table.html', response)
63+
condition1=condition2, condition2=condition1, pvalue__lte=pvalue_threshold)
64+
65+
expression = expression.annotate(abs_fold_change=Func(F('fold_change'), function='ABS')).order_by('-abs_fold_change')[:max_genes]
66+
response = dict()
67+
if expression:
68+
response_to_render = { 'expressions' : expression, 'database': dataset }
69+
response['table'] = render_to_string('NetExplorer/experiment_dge_table.html', response_to_render)
70+
response['volcano'] = do_volcano_plot(expression).plot()
71+
try:
72+
json.dumps(response)
73+
except Exception as err:
74+
print(err)
75+
return HttpResponse(json.dumps(response), content_type="application/json")
3076
else:
3177
return render(request, 'NetExplorer/404.html')

0 commit comments

Comments
 (0)