diff --git a/ras_analysis.sh b/ras_analysis.sh
index 5632baa..bb615b3 100755
--- a/ras_analysis.sh
+++ b/ras_analysis.sh
@@ -14,12 +14,16 @@ ras_diseases='BLCA,CESC,COAD,ESCA,HNSC,LUAD,LUSC,OV,PAAD,PCPG,READ,SKCM,STAD,TGC
# 1. PanCancer NF1 Classification
python scripts/pancancer_classifier.py --genes 'NF1' --drop --copy_number \
--diseases $nf1_diseases --alphas $alphas --l1_ratios $l1_mixing \
- --remove_hyper --alt_folder 'classifiers/NF1'
+ --remove_hyper --shuffled --alt_folder 'classifiers/NF1_shuffled' \
+ --keep_intermediate
# 2. PanCancer RAS Classification and predict NF1 using RAS classifier
python scripts/pancancer_classifier.py --genes 'KRAS,HRAS,NRAS' --drop \
- --remove_hyper --copy_number --alphas $alphas --l1_ratios $l1_mixing \
- --alt_genes 'NF1' --alt_diseases $nf1_diseases --alt_folder 'classifiers/RAS'
+ --diseases $ras_diseases --copy_number --remove_hyper \
+ --alphas $alphas --l1_ratios $l1_mixing \
+ --shuffled --alt_genes 'NF1' --alt_diseases $nf1_diseases \
+ --alt_folder 'classifiers/RAS_shuffled' \
+ --keep_intermediate
# 3. Within cancer-type NF1 Classification
python scripts/within_tissue_analysis.py --genes 'NF1' \
@@ -57,7 +61,8 @@ ras_no_thca_skcm=${ras_no_thca_skcm/THCA,}
python scripts/pancancer_classifier.py --genes 'KRAS,HRAS,NRAS' --drop \
--remove_hyper --copy_number --alphas $alphas --l1_ratio $l1_mixing \
- --diseases $ras_no_thca_skcm --alt_folder 'classifiers/RAS_noTHCASKCM'
+ --diseases $ras_no_thca_skcm --shuffled --alt_folder 'classifiers/RAS_noTHCASKCM_shuffled' \
+ --keep_intermediate
python scripts/apply_weights.py --classifier 'classifiers/RAS_noTHCASKCM' --copy_number
python scripts/map_mutation_class.py --scores 'classifiers/RAS_noTHCASKCM' \
@@ -69,3 +74,51 @@ Rscript --vanilla scripts/viz/ras_summary_figures.R
Rscript --vanilla scripts/viz/nf1_summary_figures.R
Rscript --vanilla scripts/viz/braf_summary_figures.R
+# 10. Additional Benchmarking Analysis
+# Randomly shuffle input RNAseq features and build a classifier
+python scripts/pancancer_classifier.py --genes 'KRAS,HRAS,NRAS' \
+ --diseases $ras_diseases --copy_number --remove_hyper \
+ --alphas $alphas --l1_ratios $l1_mixing \
+ --shuffled_before_training \
+ --keep_intermediate --alt_folder 'classifiers/RAS_shuffled_before_training'
+
+# Do not include copy number in the classifier construction
+# Note that the shuffled flag here makes classifier predictions on shuffled RNAseq data
+python scripts/pancancer_classifier.py --genes 'KRAS,HRAS,NRAS' --drop \
+ --diseases $ras_diseases \
+ --alphas $alphas --l1_ratios $l1_mixing --remove_hyper \
+ --shuffled --alt_folder 'classifiers/RAS_nocopy' --keep_intermediate
+
+# Do not include mutation in the classifier construction
+python scripts/pancancer_classifier.py --genes 'KRAS,HRAS,NRAS' --drop \
+ --diseases $ras_diseases --copy_number --no_mutation \
+ --alphas $alphas --l1_ratios $l1_mixing --remove_hyper \
+ --shuffled --alt_folder 'classifiers/RAS_nomutation' --keep_intermediate
+
+# Drop all Rasopathy genes
+python scripts/pancancer_classifier.py --genes 'KRAS,HRAS,NRAS' --drop --drop_rasopathy \
+ --diseases $ras_diseases --copy_number --remove_hyper \
+ --alphas $alphas --l1_ratios $l1_mixing --shuffled \
+ --alt_folder 'classifiers/RAS_droprasopathy'
+
+# Use only covariate information
+python scripts/pancancer_classifier.py --genes 'KRAS,HRAS,NRAS' --drop \
+ --diseases $ras_diseases --copy_number --remove_hyper \
+ --alphas $alphas --l1_ratios $l1_mixing \
+ --shuffled --keep_intermediate --drop_expression \
+ --alt_folder 'classifiers/RAS_onlycovariate'
+
+# Use only gene expression information
+python scripts/pancancer_classifier.py --genes 'KRAS,HRAS,NRAS' --drop \
+ --diseases $ras_diseases --copy_number --remove_hyper \
+ --alphas $alphas --l1_ratios $l1_mixing \
+ --shuffled --keep_intermediate --drop_covariate \
+ --alt_folder 'classifiers/RAS_onlyexpression'
+
+# Drop no genes
+python scripts/pancancer_classifier.py --genes 'KRAS,HRAS,NRAS' \
+ --diseases $ras_diseases --copy_number --remove_hyper \
+ --alphas $alphas --l1_ratios $l1_mixing \
+ --shuffled --keep_intermediate \
+ --alt_folder 'classifiers/RAS_nodrop' \
+
diff --git a/scripts/pancancer_classifier.py b/scripts/pancancer_classifier.py
index 75f15ab..514c4b1 100644
--- a/scripts/pancancer_classifier.py
+++ b/scripts/pancancer_classifier.py
@@ -53,6 +53,7 @@
import os
import sys
import warnings
+from random import sample
import pandas as pd
import csv
@@ -69,6 +70,7 @@
sys.path.insert(0, os.path.join('scripts', 'util'))
from tcga_util import get_threshold_metrics, integrate_copy_number
+from tcga_util import shuffle_columns
parser = argparse.ArgumentParser()
parser.add_argument('-g', '--genes',
@@ -111,6 +113,18 @@
help='Filename of features to use in model')
parser.add_argument('-y', '--y_matrix', default='default',
help='Either `default` or `xena` based on underlying data')
+parser.add_argument('-e', '--shuffled', action='store_true',
+ help='Shuffle the input gene expression matrix alongside')
+parser.add_argument('--shuffled_before_training', action='store_true',
+ help='Shuffle the gene expression matrix before training')
+parser.add_argument('-m', '--no_mutation', action='store_false',
+ help='Remove mutation data from y matrix')
+parser.add_argument('-z', '--drop_rasopathy', action='store_true',
+ help='Decision to drop all rasopathy genes from X matrix')
+parser.add_argument('-q', '--drop_expression', action='store_true',
+ help='Decision to drop all gene expression values from X')
+parser.add_argument('-j', '--drop_covariates', action='store_true',
+ help='Decision to drop all covariate information from X')
args = parser.parse_args()
# Load command arguments
@@ -118,6 +132,7 @@
diseases = args.diseases.split(',')
folds = int(args.folds)
drop = args.drop
+drop_rasopathy = args.drop_rasopathy
copy_number = args.copy_number
filter_count = int(args.filter_count)
filter_prop = float(args.filter_prop)
@@ -133,6 +148,11 @@
keep_inter = args.keep_intermediate
x_matrix = args.x_matrix
y_matrix = args.y_matrix
+shuffled = args.shuffled
+shuffled_before_training = args.shuffled_before_training
+no_mutation = args.no_mutation
+drop_expression = args.drop_expression
+drop_covariates = args.drop_covariates
warnings.filterwarnings('ignore',
message='Changing the shape of non-C contiguous array')
@@ -155,13 +175,13 @@
os.makedirs(disease_folder)
count_table_file = os.path.join(base_folder, 'summary_counts.csv')
-cv_heatmap_file = os.path.join(base_folder, 'cv_heatmap.svg')
-full_roc_file = os.path.join(base_folder, 'all_disease_roc.svg')
-full_pr_file = os.path.join(base_folder, 'all_disease_pr.svg')
+cv_heatmap_file = os.path.join(base_folder, 'cv_heatmap.pdf')
+full_roc_file = os.path.join(base_folder, 'all_disease_roc.pdf')
+full_pr_file = os.path.join(base_folder, 'all_disease_pr.pdf')
disease_roc_file = os.path.join(base_folder, 'disease', 'classifier_roc_')
disease_pr_file = os.path.join(base_folder, 'disease', 'classifier_pr_')
-dis_summary_auroc_file = os.path.join(base_folder, 'disease_auroc.svg')
-dis_summary_aupr_file = os.path.join(base_folder, 'disease_aupr.svg')
+dis_summary_auroc_file = os.path.join(base_folder, 'disease_auroc.pdf')
+dis_summary_aupr_file = os.path.join(base_folder, 'disease_aupr.pdf')
classifier_file = os.path.join(base_folder, 'classifier_coefficients.tsv')
roc_results_file = os.path.join(base_folder, 'pancan_roc_results.tsv')
@@ -170,9 +190,9 @@
args.alt_diseases.replace(',', '_'))
alt_count_table_file = os.path.join(base_folder, 'alt_summary_counts.csv')
alt_gene_auroc_file = os.path.join(base_folder,
- '{}_auroc_bar.svg'.format(alt_gene_base))
+ '{}_auroc_bar.pdf'.format(alt_gene_base))
alt_gene_aupr_file = os.path.join(base_folder,
- '{}_aupr_bar.svg'.format(alt_gene_base))
+ '{}_aupr_bar.pdf'.format(alt_gene_base))
alt_gene_summary_file = os.path.join(base_folder,
'{}_summary.tsv'.format(alt_gene_base))
@@ -220,6 +240,13 @@
if x_matrix == 'raw':
rnaseq_full_df.drop(common_genes, axis=1, inplace=True)
+if drop_rasopathy:
+ rasopathy_genes = set(['BRAF', 'CBL', 'HRAS', 'KRAS', 'MAP2K1', 'MAP2K2',
+ 'NF1', 'NRAS', 'PTPN11', 'RAF1', 'SHOC2', 'SOS1',
+ 'SPRED1', 'RIT1'])
+ rasopathy_drop = list(rasopathy_genes.intersection(rnaseq_full_df.columns))
+ rnaseq_full_df.drop(rasopathy_drop, axis=1, inplace=True)
+
# Incorporate copy number for gene activation/inactivation
if copy_number:
# Load copy number matrices
@@ -236,7 +263,8 @@
y = integrate_copy_number(y=y, cancer_genes_df=cancer_genes,
genes=common_genes, loss_df=copy_loss_df,
- gain_df=copy_gain_df)
+ gain_df=copy_gain_df,
+ include_mutation=no_mutation)
# Process y matrix
y = y.assign(total_status=y.max(axis=1))
@@ -295,6 +323,21 @@
x_df_update.index = x_df.index
x_df = x_df_update.merge(covar, left_index=True, right_index=True)
+# Remove information from the X matrix given input arguments
+if drop_expression:
+ x_df = x_df.iloc[:, num_features_kept:]
+elif drop_covariates:
+ x_df = x_df.iloc[:, 0:num_features_kept]
+
+# Shuffle expression matrix _before_ training - this can be used as NULL model
+if shuffled_before_training:
+ # Shuffle genes
+ x_train_genes = x_df.iloc[:, 0:num_features_kept]
+ rnaseq_shuffled_df = x_train_genes.apply(shuffle_columns, axis=1)
+
+ x_train_cov = x_df.iloc[:, num_features_kept:]
+ x_df = pd.concat([rnaseq_shuffled_df, x_train_cov], axis=1)
+
# Build classifier pipeline
x_train, x_test, y_train, y_test = train_test_split(x_df, y_df, test_size=0.1,
random_state=0,
@@ -326,7 +369,7 @@
ax.set_xlabel('Regularization strength multiplier (alpha)')
ax.set_ylabel('Elastic net mixing parameter (l1_ratio)')
plt.tight_layout()
-plt.savefig(cv_heatmap_file, dpi=600, format='svg', bbox_inches='tight')
+plt.savefig(cv_heatmap_file, dpi=600, bbox_inches='tight')
plt.close()
# Get predictions
@@ -345,6 +388,21 @@
metrics_cv = get_threshold_metrics(y_train, y_cv,
drop_intermediate=keep_inter)
+# Determine shuffled predictive ability of shuffled gene expression matrix
+# representing a test of inflation of ROC metrics. Be sure to only shuffle
+# gene names, retain covariate information (tissue type and log10 mutations)
+if shuffled:
+ # Shuffle genes
+ x_train_genes = x_train.iloc[:, 0:num_features_kept]
+ rnaseq_shuffled_df = x_train_genes.apply(shuffle_columns, axis=1)
+
+ x_train_cov = x_train.iloc[:, num_features_kept:]
+ rnaseq_shuffled_df = pd.concat([rnaseq_shuffled_df, x_train_cov], axis=1)
+
+ y_predict_shuffled = cv_pipeline.decision_function(rnaseq_shuffled_df)
+ metrics_shuffled = get_threshold_metrics(y_train, y_predict_shuffled,
+ drop_intermediate=keep_inter)
+
# Decide to save ROC results to file
if keep_inter:
train_roc = metrics_train['roc_df']
@@ -354,16 +412,25 @@
cv_roc = metrics_cv['roc_df']
cv_roc = cv_roc.assign(train_type='cv')
full_roc_df = pd.concat([train_roc, test_roc, cv_roc])
+ if shuffled:
+ shuffled_roc = metrics_shuffled['roc_df']
+ shuffled_roc = shuffled_roc.assign(train_type='shuffled')
+ full_roc_df = pd.concat([full_roc_df, shuffled_roc])
full_roc_df = full_roc_df.assign(disease='PanCan')
# Plot ROC
sns.set_style("whitegrid")
plt.figure(figsize=(2.7, 2.4))
total_auroc = {}
-colors = ['blue', 'green', 'orange']
+colors = ['blue', 'green', 'orange', 'grey']
idx = 0
-for label, metrics in [('Training', metrics_train), ('Testing', metrics_test),
- ('CV', metrics_cv)]:
+
+metrics_list = [('Training', metrics_train), ('Testing', metrics_test),
+ ('CV', metrics_cv)]
+if shuffled:
+ metrics_list += [('Random', metrics_shuffled)]
+
+for label, metrics in metrics_list:
roc_df = metrics['roc_df']
plt.plot(roc_df.fpr, roc_df.tpr,
@@ -371,6 +438,8 @@
linewidth=2, c=colors[idx])
total_auroc[label] = metrics['auroc']
idx += 1
+
+plt.plot([0, 1], [0, 1], color='navy', linewidth=2, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate', fontsize=8)
@@ -380,24 +449,29 @@
plt.legend(bbox_to_anchor=(0.2, -0.45, 0.7, .202), loc=0, borderaxespad=0.,
fontsize=7.5)
plt.tight_layout()
-plt.savefig(full_roc_file, dpi=600, format='svg', bbox_inches='tight')
+plt.savefig(full_roc_file, dpi=600, bbox_inches='tight')
plt.close()
# Plot PR
sns.set_style("whitegrid")
plt.figure(figsize=(2.7, 2.4))
total_aupr = {}
-colors = ['blue', 'green', 'orange']
+colors = ['blue', 'green', 'orange', 'grey']
idx = 0
-for label, metrics in [('Training', metrics_train), ('Testing', metrics_test),
- ('CV', metrics_cv)]:
+metrics_list = [('Training', metrics_train), ('Testing', metrics_test),
+ ('CV', metrics_cv)]
+if shuffled:
+ metrics_list += [('Random', metrics_shuffled)]
+
+for label, metrics in metrics_list:
pr_df = metrics['pr_df']
plt.plot(pr_df.recall, pr_df.precision,
label='{} (AUPR = {:.1%})'.format(label, metrics['aupr']),
linewidth=2, c=colors[idx])
total_aupr[label] = metrics['aupr']
idx += 1
+
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('Recall', fontsize=8)
@@ -407,7 +481,7 @@
plt.legend(bbox_to_anchor=(0.2, -0.45, 0.7, .202), loc=0, borderaxespad=0.,
fontsize=7.5)
plt.tight_layout()
-plt.savefig(full_pr_file, dpi=600, format='svg', bbox_inches='tight')
+plt.savefig(full_pr_file, dpi=600, bbox_inches='tight')
plt.close()
# disease specific performance
@@ -418,10 +492,14 @@
# Get true and predicted training labels
y_disease_train = y_train[y_train.index.isin(sample_sub)]
+ if y_disease_train.sum() < 1:
+ continue
y_disease_predict_train = y_predict_train[y_train.index.isin(sample_sub)]
# Get true and predicted testing labels
y_disease_test = y_test[y_test.index.isin(sample_sub)]
+ if y_disease_test.sum() < 1:
+ continue
y_disease_predict_test = y_predict_test[y_test.index.isin(sample_sub)]
# Get predicted labels for samples when they were in cross validation set
@@ -442,6 +520,14 @@
disease=disease,
drop_intermediate=keep_inter)
+ # Get predictions and metrics with shuffled gene expression
+ if shuffled:
+ y_dis_predict_shuf = y_predict_shuffled[y_train.index.isin(sample_sub)]
+ met_shuff_dis = get_threshold_metrics(y_disease_train,
+ y_dis_predict_shuf,
+ disease=disease,
+ drop_intermediate=keep_inter)
+
if keep_inter:
train_roc = met_train_dis['roc_df']
train_roc = train_roc.assign(train_type='train')
@@ -450,25 +536,41 @@
cv_roc = met_cv_dis['roc_df']
cv_roc = cv_roc.assign(train_type='cv')
full_dis_roc_df = train_roc.append(test_roc).append(cv_roc)
+
+ if shuffled:
+ shuffled_roc = met_shuff_dis['roc_df']
+ shuffled_roc = shuffled_roc.assign(train_type='shuffled')
+ full_dis_roc_df = full_dis_roc_df.append(shuffled_roc)
+
full_dis_roc_df = full_dis_roc_df.assign(disease=disease)
full_roc_df = full_roc_df.append(full_dis_roc_df)
# Store results in disease indexed dictionary
disease_metrics[disease] = [met_train_dis, met_test_dis, met_cv_dis]
+ if shuffled:
+ disease_metrics[disease] += [met_shuff_dis]
+
disease_auroc = {}
disease_aupr = {}
for disease, metrics_val in disease_metrics.items():
- met_train, met_test, met_cv = metrics_val
- disease_pr_sub_file = '{}_pred_{}.svg'.format(disease_pr_file, disease)
- disease_roc_sub_file = '{}_pred_{}.svg'.format(disease_roc_file, disease)
+
+ labels = ['Training', 'Testing', 'CV', 'Random']
+ met_list = []
+ idx = 0
+ for met in metrics_val:
+ lab = labels[idx]
+ met_list.append((lab, met))
+ idx += 1
+
+ disease_pr_sub_file = '{}_pred_{}.pdf'.format(disease_pr_file, disease)
+ disease_roc_sub_file = '{}_pred_{}.pdf'.format(disease_roc_file, disease)
# Plot disease specific PR
plt.figure(figsize=(2.7, 2.4))
aupr = []
idx = 0
- for label, metrics in [('Training', met_train), ('Testing', met_test),
- ('CV', met_cv)]:
+ for label, metrics in met_list:
pr_df = metrics['pr_df']
plt.plot(pr_df.recall, pr_df.precision,
label='{} (AUPR = {:.1%})'.format(label, metrics['aupr']),
@@ -486,16 +588,14 @@
plt.legend(bbox_to_anchor=(0.2, -0.45, 0.7, .202), loc=0, borderaxespad=0.,
fontsize=7.5)
plt.tight_layout()
- plt.savefig(disease_pr_sub_file, dpi=600, format='svg',
- bbox_inches='tight')
+ plt.savefig(disease_pr_sub_file, dpi=600, bbox_inches='tight')
plt.close()
# Plot disease specific ROC
plt.figure(figsize=(2.7, 2.4))
auroc = []
idx = 0
- for label, metrics in [('Training', met_train), ('Testing', met_test),
- ('CV', met_cv)]:
+ for label, metrics in met_list:
roc_df = metrics['roc_df']
plt.plot(roc_df.fpr, roc_df.tpr,
label='{} (AUROC = {:.1%})'.format(label, metrics['auroc']),
@@ -504,6 +604,7 @@
idx += 1
disease_auroc[disease] = auroc
+ plt.plot([0, 1], [0, 1], color='navy', linewidth=2, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate', fontsize=8)
@@ -513,28 +614,30 @@
plt.legend(bbox_to_anchor=(0.2, -0.45, 0.7, .202), loc=0, borderaxespad=0.,
fontsize=7.5)
plt.tight_layout()
- plt.savefig(disease_roc_sub_file, dpi=600, format='svg',
- bbox_inches='tight')
+ plt.savefig(disease_roc_sub_file, dpi=600, bbox_inches='tight')
plt.close()
-disease_auroc_df = pd.DataFrame(disease_auroc, index=['Train', 'Test',
- 'Cross Validation']).T
+index_lab = ['Train', 'Test', 'Cross Validation']
+
+if shuffled:
+ index_lab += ['Random']
+
+disease_auroc_df = pd.DataFrame(disease_auroc, index=index_lab).T
disease_auroc_df = disease_auroc_df.sort_values('Cross Validation',
ascending=False)
ax = disease_auroc_df.plot(kind='bar', title='Disease Specific Performance')
ax.set_ylabel('AUROC')
plt.tight_layout()
-plt.savefig(dis_summary_auroc_file, dpi=600, format='svg', bbox_inches='tight')
+plt.savefig(dis_summary_auroc_file, dpi=600, bbox_inches='tight')
plt.close()
-disease_aupr_df = pd.DataFrame(disease_aupr, index=['Train', 'Test',
- 'Cross Validation']).T
+disease_aupr_df = pd.DataFrame(disease_aupr, index=index_lab).T
disease_aupr_df = disease_aupr_df.sort_values('Cross Validation',
ascending=False)
ax = disease_aupr_df.plot(kind='bar', title='Disease Specific Performance')
ax.set_ylabel('AUPR')
plt.tight_layout()
-plt.savefig(dis_summary_aupr_file, dpi=600, format='svg', bbox_inches='tight')
+plt.savefig(dis_summary_aupr_file, dpi=600, bbox_inches='tight')
plt.close()
# Save classifier coefficients
@@ -636,6 +739,10 @@
y_sub = y_test[y_test.index.isin(sample_dis)]
category = 'Holdout'
+ # If there are not enough classes do not proceed to plot
+ if y_sub.sum() < 1:
+ continue
+
neg, pos = y_sub.value_counts()
val_x_type[disease] = [category, neg, pos]
y_pred_alt = cv_pipeline.decision_function(x_sub)
@@ -670,8 +777,7 @@
ax.set_ylim([0, 1])
ax.set_ylabel('AUROC')
plt.tight_layout()
- plt.savefig(alt_gene_auroc_file, dpi=600, format='svg',
- bbox_inches='tight')
+ plt.savefig(alt_gene_auroc_file, dpi=600, bbox_inches='tight')
plt.close()
# Plot alternative gene cancer-type specific AUPR plots
@@ -683,8 +789,7 @@
ax.set_ylim([0, 1])
ax.set_ylabel('AUPR')
plt.tight_layout()
- plt.savefig(alt_gene_aupr_file, dpi=600, format='svg',
- bbox_inches='tight')
+ plt.savefig(alt_gene_aupr_file, dpi=600, bbox_inches='tight')
plt.close()
# Write a summary for the inputs and outputs of the classifier
diff --git a/scripts/ras_differential_expression.ipynb b/scripts/ras_differential_expression.ipynb
new file mode 100644
index 0000000..d98ae0f
--- /dev/null
+++ b/scripts/ras_differential_expression.ipynb
@@ -0,0 +1,416 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Ras Differential Expression Analysis\n",
+ "\n",
+ "Perform a t-test across all genes to determine differentially expressed genes between Ras wildtype and Ras mutant genes. Compare with what the model learns and output the data for visualization with R/ggplot2."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/home/gway/anaconda3/lib/python3.5/site-packages/statsmodels/compat/pandas.py:56: FutureWarning: The pandas.core.datetools module is deprecated and will be removed in a future version. Please use the pandas.tseries module instead.\n",
+ " from pandas.core import datetools\n"
+ ]
+ }
+ ],
+ "source": [
+ "import os\n",
+ "import sys\n",
+ "import pandas as pd\n",
+ "import scipy\n",
+ "from sklearn.preprocessing import StandardScaler\n",
+ "\n",
+ "import matplotlib.pyplot as plt\n",
+ "import seaborn as sns\n",
+ "import plotnine as gg\n",
+ "\n",
+ "sys.path.insert(0, os.path.join('..', 'scripts', 'util'))\n",
+ "from tcga_util import integrate_copy_number"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "%matplotlib inline\n",
+ "plt.style.use('seaborn-notebook')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Load Data\n",
+ "\n",
+ "Loading RNAseq (X matrix), copy number and mutation (Y matrix), and coefficients data (W matrix from machine learning model)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Load RNAseq matrix\n",
+ "expr_file = os.path.join('..', 'data', 'pancan_rnaseq_freeze.tsv')\n",
+ "rnaseq_full_df = pd.read_table(expr_file, index_col=0)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "# Load Mutation matrix\n",
+ "mut_file = os.path.join('..', 'data', 'pancan_mutation_freeze.tsv')\n",
+ "mutation_df = pd.read_table(mut_file, index_col=0)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "# Load sample freeze data and cancer genes\n",
+ "sample_freeze_file = os.path.join('..', 'data', 'sample_freeze.tsv')\n",
+ "sample_freeze = pd.read_table(sample_freeze_file, index_col=0)\n",
+ "\n",
+ "cancer_gene_file = os.path.join('..', 'data', 'vogelstein_cancergenes.tsv')\n",
+ "cancer_genes = pd.read_table(cancer_gene_file)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "# Load copy number data to determine final status matrix\n",
+ "copy_loss_file = os.path.join('..', 'data', 'copy_number_loss_status.tsv')\n",
+ "copy_gain_file = os.path.join('..', 'data', 'copy_number_gain_status.tsv')\n",
+ "\n",
+ "copy_loss_df = pd.read_table(copy_loss_file, index_col=0)\n",
+ "copy_gain_df = pd.read_table(copy_gain_file, index_col=0)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "# Load Coefficients File\n",
+ "coef_file = os.path.join('..', 'classifiers', 'RAS', 'classifier_coefficients.tsv')\n",
+ "coef_df = pd.read_table(coef_file, index_col=0)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Process and Prep Data for Differential Expression Analysis"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Process y matrix\n",
+ "genes = ['KRAS', 'HRAS', 'NRAS']\n",
+ "y = mutation_df[genes]\n",
+ "\n",
+ "y_df = integrate_copy_number(y=y, cancer_genes_df=cancer_genes,\n",
+ " genes=genes, loss_df=copy_loss_df,\n",
+ " gain_df=copy_gain_df)\n",
+ "\n",
+ "y_df = y_df.assign(total_status=y.max(axis=1))\n",
+ "y_df = y_df.reset_index().merge(sample_freeze,\n",
+ " how='left').set_index('SAMPLE_BARCODE')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "0 8008\n",
+ "1 1066\n",
+ "Name: total_status, dtype: int64"
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "y_df['total_status'].value_counts()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Subset X matrix to coefficients\n",
+ "x_df = rnaseq_full_df.loc[:, coef_df['feature']]\n",
+ "x_df = x_df.dropna(axis=1)\n",
+ "\n",
+ "x_df_update = StandardScaler().fit_transform(x_df)\n",
+ "x_df_update = pd.DataFrame(x_df_update, columns=x_df.columns, index=x_df.index)\n",
+ "x_df = x_df_update"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Get two arrays to compare\n",
+ "ras_wt_samples = y_df[y_df['total_status'] == 0].index\n",
+ "ras_mut_samples = y_df[y_df['total_status'] == 1].index\n",
+ "\n",
+ "x_wt_df = x_df.loc[ras_wt_samples, :]\n",
+ "x_mut_df = x_df.loc[ras_mut_samples, :]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Perform the analysis and output results"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "ttest_results = scipy.stats.ttest_ind(x_mut_df, x_wt_df)\n",
+ "t_stat = ttest_results.statistic\n",
+ "p_val = ttest_results.pvalue"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "ttest_df = pd.DataFrame(t_stat, columns=['stat'])\n",
+ "ttest_df = ttest_df.assign(pval = p_val)\n",
+ "ttest_df = ttest_df.assign(gene = x_wt_df.columns)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " stat | \n",
+ " pval | \n",
+ " gene | \n",
+ " feature | \n",
+ " weight | \n",
+ " abs | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " 0 | \n",
+ " 6.812033 | \n",
+ " 1.023496e-11 | \n",
+ " PBX3 | \n",
+ " PBX3 | \n",
+ " 0.139761 | \n",
+ " 0.139761 | \n",
+ "
\n",
+ " \n",
+ " 1 | \n",
+ " 21.001785 | \n",
+ " 1.164957e-95 | \n",
+ " SPRY2 | \n",
+ " SPRY2 | \n",
+ " 0.123713 | \n",
+ " 0.123713 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " stat pval gene feature weight abs\n",
+ "0 6.812033 1.023496e-11 PBX3 PBX3 0.139761 0.139761\n",
+ "1 21.001785 1.164957e-95 SPRY2 SPRY2 0.123713 0.123713"
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "plot_df = pd.merge(ttest_df, coef_df, left_on='gene', right_on='feature')\n",
+ "plot_df.head(2)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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\ncuTI+o5HENzKlY7JdVUuTq1WX3WTiSRJWCwWLly4QKtWrerkOYVLrZL69+/v7jCEBuLSx0OdTieq\nhQu3hOqKCV/JarXWyXPFxcVht9urnSZzOBxoNBq8vLyw2Wx18nyCcCtyKck98sgjLm2tFoSbnStV\n8StLyt2odu3a0aVLF8rKypyqbTgcDsrKyrjtttvQarU1ltVrLBwOBz/88AOTJ0/moYce4oknnmDj\nxo2ig7rQKLg0XRkWFsbmzZuZMGEC/fr1q/aNYMyYMXUenCA0NH9/f9LS0mq9pi5IkkR8fDwDBw4k\nPT1dWetWqVS0b98erVbLAw880Kj71zkcDt59911+/vlnDAYDISEhWK1WVq9eze7du3nnnXeU9UZZ\nljl16hS//PILVquV7t27c+edd4quDkK9qrULAUD79u2v/iD1uLuyPokuBMKVwsPDyc7Ovuo1ISEh\n5OTk1Nlz5uTk8OKLL5KcnIxarcbb2xu1Ws29997L9OnTcTgc7N+/n1OnTuHr60ufPn1o1qxZnT3/\njUhMTGTBggVVtuUDZGRk8PjjjzN27FjMZjNvvvkmJ06cQKPRoFKpsFqtGAwGFi1aRFhYmJt+AqEx\nqY/3ZJeSXOX5nauJioqqk4AakkhywpV8fHxqPSvn5eWF2Wyu0+d1OBwcO3aMEydO4OXlRVxcHJGR\nkSQnJzN37lyKiorQarXYbDZkWea+++5j6tSpbt91+fLLL3PhwoVqW23ZbDbKysr47LPPeO+999iz\nZ0+VZFZQUEBAQAAffvih238Wwf3c0moHbs4EJgjXw2Kx1HqNK5tTrpVKpaJLly506dJFuc1sNvPq\nq68iSRIRERHK7bIs8/333xMREeH2ZQKj0Vjl+EMljUaD2WwmPz+fPXv2VNu3LSAggKysLI4fP05s\nbGx9hyvcgq7po9OZM2dYv349y5cvZ/369Zw5c6a+4hIEt3Cl3Yrdbufnn3+ul2R3uZ9//pmSkpIq\nlXskSSIsLIyNGze6fedlVFRUjUcqKioq8Pf3JzU1Fbh6p5A//vijXuITBJdGcjabjTlz5rB161an\nNwFJkhg+fDhvv/32Vat7C8LNQqfTubQr8L333iMoKIjFixc7jbLq0uHDh2ts5KnRaKioqMBoNNbb\n87vioYce4tVXX8Xf379KEjMajTz99NO1Fo9wOBxNtmGp4H4ujeSWLVvGd999x4wZM9ixYwdHjx5l\n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\n5AiCIAiT9f8AYHCTGDnsIPAAAAAASUVORK5CYII=\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 15,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "p = (gg.ggplot(plot_df, gg.aes(x='weight', y='stat')) +\n",
+ " gg.geom_point(size=4, alpha=0.6) +\n",
+ " gg.theme_seaborn(style='whitegrid') +\n",
+ " gg.xlab('Ras Classifier Weight') +\n",
+ " gg.ylab('Differential Expression Score') +\n",
+ " gg.ggtitle('') +\n",
+ " gg.theme(\n",
+ " plot_title=gg.element_text(size=22),\n",
+ " axis_title_x=gg.element_text(size=16),\n",
+ " axis_title_y=gg.element_text(size=16),\n",
+ " axis_text_x=gg.element_text(size=14),\n",
+ " axis_text_y=gg.element_text(size=14),\n",
+ " axis_ticks_length=4,\n",
+ " legend_position=(1.0, 0.5),\n",
+ " legend_background=gg.element_blank(),\n",
+ " legend_key=gg.element_rect(fill='white'),\n",
+ " legend_text=gg.element_text(size=9),\n",
+ " legend_title=gg.element_text(size=12),\n",
+ " panel_border=gg.element_blank(),\n",
+ " panel_grid_major=gg.element_blank(),\n",
+ " panel_grid_minor=gg.element_blank()))\n",
+ "p"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "# Output data for ggplot\n",
+ "diff_exp_file = os.path.join('..', 'classifiers', 'RAS', 'differential_expression.tsv')\n",
+ "plot_df.to_csv(diff_exp_file, sep='\\t', index=False)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python [default]",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.5.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/scripts/util/tcga_util.py b/scripts/util/tcga_util.py
index 403e1cc..ad374b6 100644
--- a/scripts/util/tcga_util.py
+++ b/scripts/util/tcga_util.py
@@ -47,7 +47,8 @@ def get_threshold_metrics(y_true, y_pred, drop_intermediate=False,
'pr_df': pr_df, 'disease': disease}
-def integrate_copy_number(y, cancer_genes_df, genes, loss_df, gain_df):
+def integrate_copy_number(y, cancer_genes_df, genes, loss_df, gain_df,
+ include_mutation=True):
"""
Function to integrate copy number data to define gene activation or gene
inactivation events. Copy number loss results in gene inactivation events
@@ -61,6 +62,7 @@ def integrate_copy_number(y, cancer_genes_df, genes, loss_df, gain_df):
genes - the input list of genes to build the classifier for
loss_df - a sample by gene dataframe listing copy number loss events
gain_df - a sample by gene dataframe listing copy number gain events
+ include_mutation - boolean to decide to include mutation status
"""
# Find if the input genes are in this master list
@@ -87,4 +89,15 @@ def integrate_copy_number(y, cancer_genes_df, genes, loss_df, gain_df):
y = y.fillna(0)
y = y.astype(int)
+ if not include_mutation:
+ y = y.drop(genes, axis=1)
return y
+
+
+def shuffle_columns(gene):
+ """
+ To be used in an `apply` pandas func to shuffle columns around a datafame
+ Import only
+ """
+ import numpy as np
+ return np.random.permutation(gene.tolist())
diff --git a/scripts/viz/ras_benchmarking_figures.R b/scripts/viz/ras_benchmarking_figures.R
new file mode 100644
index 0000000..71c0f78
--- /dev/null
+++ b/scripts/viz/ras_benchmarking_figures.R
@@ -0,0 +1,331 @@
+# Gregory Way 2017
+# PanCancer Classifier
+# scripts/viz/ras_benchmarking_figures.R
+#
+# Compares performance of Ras classifier in 3 different scenarios
+#
+# 1) Mutation vs. Copy Number vs. Both
+# 2) Dropping Ras vs. Dropping Rasopathy vs. no Drop
+# 3) Expression vs. Covariates vs. Both
+#
+# Also compares classifier coefficients across for comparisons 1 and 2
+# (none of the coefficients are the same for 3)
+#
+# Usage: Run in command line
+#
+# Rscript --vanilla scripts/viz/ras_benchmarking_figures.R
+#
+# Output:
+# 5 Plots given above
+
+library(dplyr)
+library(ggplot2)
+library(ggrepel)
+
+source(file.path("scripts", "util", "pancancer_util.R"))
+
+getFeatureResults <- function(results_directory, features, gene) {
+ # Process results - will obtain a dataframe for outputing an ROC curve,
+ # the cross validation AUROC, and will list the number of nonzero feature
+ #
+ # Arguments:
+ # results_directory - the location of the base directory where results are
+ # features - a string indicating the the dimensionality reduction algorithm
+ # gene - a string indicating gene name
+ #
+ # Output:
+ # A list with ROC results and a dataframe with nonzero count and algorithm,
+ # and cross validation AUROC
+
+ roc_file <- file.path(results_directory, "pancan_roc_results.tsv")
+ feature_file <- file.path(results_directory, "classifier_coefficients.tsv")
+ summary_file <- file.path(results_directory, "classifier_summary.txt")
+
+ # Obtain the ROC for the given file and process dataframe
+ roc_ <- readr::read_tsv(roc_file)
+ roc_ <- roc_ %>% dplyr::mutate(Features = features)
+ roc_ <- roc_ %>% dplyr::mutate(gene = gene)
+
+ # Obtain the feature coefficients file
+ coef_ <- readr::read_tsv(feature_file)
+ coef_ <- coef_ %>% dplyr::mutate(Features = features)
+ coef_ <- coef_ %>% dplyr::mutate(gene = gene)
+
+ # Get the classifier summary
+ classifier_summary <- parse_summary(summary_file)
+ train_auroc <- classifier_summary$`Training AUROC`
+ test_auroc <- classifier_summary$`Testing AUROC`
+ train_auroc <- as.data.frame(round(as.numeric(train_auroc) * 100, 1))
+ test_auroc <- as.data.frame(round(as.numeric(test_auroc) * 100, 1))
+ auroc <- cbind(train_auroc, test_auroc)
+ colnames(auroc) <- c("Train", "Test")
+ auroc$Features <- features
+
+ return(list(roc_, coef_, auroc))
+}
+
+# Set file path objects
+base_folder <- 'classifiers'
+base_ras <- file.path(base_folder, 'RAS_shuffled')
+
+ras_mutation_only <- file.path(base_folder, 'RAS_nocopy')
+ras_copy_only <- file.path(base_folder, 'RAS_nomutation')
+
+ras_expression_only <- file.path(base_folder, 'RAS_onlyexpression')
+ras_covariate_only <- file.path(base_folder, 'RAS_onlycovariate')
+
+ras_nodrop <- file.path(base_folder, "RAS_nodrop")
+ras_droprasopathy <- file.path(base_folder, "RAS_droprasopathy")
+
+set.seed(123)
+
+# Process algorithm results
+raw_results <- getFeatureResults(base_ras, "Combined", "Ras")
+mutation_only_results <- getFeatureResults(ras_mutation_only, 'Mutation Only',
+ 'Ras')
+copy_only_results <- getFeatureResults(ras_copy_only, "Copy Only", "Ras")
+
+ras_expression_only_results <- getFeatureResults(ras_expression_only,
+ "Exprs Only", "Ras")
+ras_covariate_only_results <- getFeatureResults(ras_covariate_only,
+ "Cov Only", "Ras")
+
+ras_nodrop_results <- getFeatureResults(ras_nodrop, "No Gene Drop", "Ras")
+ras_droprasopathy_results <- getFeatureResults(ras_droprasopathy,
+ "Drop Rasopathy", "Ras")
+
+# Combine DataFrames
+mut_v_copy_roc_df <- dplyr::bind_rows(raw_results[[1]],
+ mutation_only_results[[1]],
+ copy_only_results[[1]])
+mut_v_copy_coef_df <- raw_results[[2]] %>%
+ dplyr::full_join(mutation_only_results[[2]], by = 'feature',
+ suffix = c("_combined", "_mutation")) %>%
+ dplyr::full_join(copy_only_results[[2]], by = 'feature',
+ suffix = c("", "_copy"))
+mut_v_copy_auroc_df <- dplyr::bind_rows(raw_results[[3]],
+ mutation_only_results[[3]],
+ copy_only_results[[3]])
+
+exp_v_cov_roc_df <- dplyr::bind_rows(raw_results[[1]],
+ ras_expression_only_results[[1]],
+ ras_covariate_only_results[[1]])
+exp_v_cov_coef_df <- raw_results[[2]] %>%
+ dplyr::full_join(ras_expression_only_results[[2]], by = 'feature',
+ suffix = c("_combined", "_exprs")) %>%
+ dplyr::full_join(ras_covariate_only_results[[2]], by = 'feature',
+ suffix = c("", "_cov"))
+exp_v_cov_auroc_df <- dplyr::bind_rows(raw_results[[3]],
+ ras_expression_only_results[[3]],
+ ras_covariate_only_results[[3]])
+
+drop_genes_roc_df <- dplyr::bind_rows(raw_results[[1]],
+ ras_nodrop_results[[1]],
+ ras_droprasopathy_results[[1]])
+drop_genes_coef_df <- raw_results[[2]] %>%
+ dplyr::full_join(ras_nodrop_results[[2]], by = 'feature',
+ suffix = c("_combined", "_nodrop")) %>%
+ dplyr::full_join(ras_droprasopathy_results[[2]], by = 'feature',
+ suffix = c("", "_rasopathy"))
+drop_genes_auroc_df <- dplyr::bind_rows(raw_results[[3]],
+ ras_nodrop_results[[3]],
+ ras_droprasopathy_results[[3]])
+
+# Reorder auroc_df
+mut_v_copy_auroc_df <- mut_v_copy_auroc_df %>%
+ dplyr::select(Features, dplyr::everything())
+exp_v_cov_auroc_df <- exp_v_cov_auroc_df %>%
+ dplyr::select(Features, dplyr::everything())
+drop_genes_auroc_df <- drop_genes_auroc_df %>%
+ dplyr::select(Features, dplyr::everything())
+
+# Order Factors
+mut_copy_levels <- c('Combined', "Mutation Only", "Copy Only")
+exp_cov_levels <- c('Combined', "Exprs Only", "Cov Only")
+drop_gene_levels <- c('Drop Ras', 'No Gene Drop', 'Drop Rasopathy')
+train_type_levels <- c('train', 'test', 'shuffled')
+
+mut_v_copy_roc_df$Features <- factor(mut_v_copy_roc_df$Features,
+ levels = mut_copy_levels)
+mut_v_copy_roc_df$train_type <- factor(mut_v_copy_roc_df$train_type,
+ levels = train_type_levels)
+mut_v_copy_auroc_df$Features <- factor(mut_v_copy_auroc_df$Features,
+ levels = mut_copy_levels)
+
+exp_v_cov_roc_df$Features <- factor(exp_v_cov_roc_df$Features,
+ levels = exp_cov_levels)
+exp_v_cov_roc_df$train_type <- factor(exp_v_cov_roc_df$train_type,
+ levels = train_type_levels)
+exp_v_cov_auroc_df$Features <- factor(exp_v_cov_auroc_df$Features,
+ levels = exp_cov_levels)
+
+drop_genes_roc_df$Features <- drop_genes_roc_df$Features %>%
+ dplyr::recode('Combined' = 'Drop Ras')
+drop_genes_auroc_df$Features <- drop_genes_auroc_df$Features %>%
+ dplyr::recode('Combined' = 'Drop Ras')
+drop_genes_roc_df$Features <- factor(drop_genes_roc_df$Features,
+ levels = drop_gene_levels)
+drop_genes_roc_df$train_type <- factor(drop_genes_roc_df$train_type,
+ levels = train_type_levels)
+drop_genes_auroc_df$Features <- factor(drop_genes_auroc_df$Features,
+ levels = drop_gene_levels)
+
+# Subset only to pancancer and also only cross validation performance
+mut_v_copy_roc_df <- mut_v_copy_roc_df %>%
+ dplyr::filter(disease == "PanCan") %>%
+ dplyr::filter(train_type %in% train_type_levels)
+
+exp_v_cov_roc_df <- exp_v_cov_roc_df %>%
+ dplyr::filter(disease == "PanCan") %>%
+ dplyr::filter(train_type %in% train_type_levels)
+
+drop_genes_roc_df <- drop_genes_roc_df %>%
+ dplyr::filter(disease == "PanCan") %>%
+ dplyr::filter(train_type %in% train_type_levels)
+
+# Plot ROC curves and feature bar plot
+table_theme <- gridExtra::ttheme_default(base_size = 6,
+ padding = unit(c(0.65, 0.65), "mm"))
+
+plot_roc_info <- function(roc_df, auroc_df, custom_colors) {
+ table_gg <- gridExtra::tableGrob(auroc_df, rows = NULL, theme = table_theme)
+
+ feat_gg <- ggplot(roc_df, aes(x = fpr, y = tpr, color = Features)) +
+ geom_step(aes(linetype = train_type), size = 0.3) +
+ geom_segment(aes(x = 0 , y = 0, xend = 1, yend = 1),
+ linetype = "dashed", color = "black", size = 0.2) +
+ annotation_custom(table_gg,
+ xmin = 0.6, xmax = 0.7, ymin = 0.08, ymax = 0.18) +
+ scale_color_manual(values = custom_colors) +
+ scale_linetype_manual('Data Type', labels = c('Train', 'Test', 'Shuffled'),
+ values = c('solid', 'dashed', 'dotted')) +
+ scale_y_continuous(labels = scales::percent) +
+ scale_x_continuous(labels = scales::percent) +
+ xlab("False Positive Rate") +
+ ylab("True Positive Rate") +
+ coord_fixed() +
+ theme_bw() +
+ theme(axis.text = element_text(size = rel(0.5)),
+ axis.title = element_text(size = rel(0.6)),
+ axis.title.y = element_text(margin =
+ margin(t = 0, r = 0, b = 0, l = 0)),
+ axis.title.x = element_text(margin =
+ margin(t = 3, r = 0, b = 0, l = 0)),
+ legend.text = element_text(size = rel(0.6)),
+ legend.title = element_text(size = rel(0.7)),
+ legend.key = element_rect(size = 0.5),
+ legend.position = 'right',
+ legend.key.size = unit(0.7, 'lines'),
+ legend.margin = margin(l = -0.3, unit = 'cm'))
+
+ return(feat_gg)
+}
+
+# Set plotting colors
+mut_v_copy_colors <- c("Combined" = "#984ea3", "Copy Only" = "#a65628",
+ "Mutation Only" = "#326F32")
+mut_v_copy_fig <- plot_roc_info(mut_v_copy_roc_df, mut_v_copy_auroc_df,
+ mut_v_copy_colors)
+
+base_fig_folder <- file.path(base_folder, 'RAS', 'figures')
+mut_v_copy_fig_file <- file.path(base_fig_folder, 'mut_v_copy_fig.pdf')
+ggplot2::ggsave(mut_v_copy_fig_file, plot = mut_v_copy_fig, dpi = 600,
+ width = 3, height = 3)
+
+exp_v_cov_colors <- c("Combined" = "#984ea3", "Exprs Only" = "#4daf4a",
+ "Cov Only" = "#ff7f00")
+exp_v_cov_fig <- plot_roc_info(exp_v_cov_roc_df,
+ exp_v_cov_auroc_df,
+ exp_v_cov_colors)
+exp_v_cov_fig_file <- file.path(base_fig_folder, 'exp_v_cov_fig.pdf')
+ggplot2::ggsave(exp_v_cov_fig_file, plot = exp_v_cov_fig, dpi = 600,
+ width = 3, height = 3)
+
+drop_genes_colors <- c("Drop Ras" = "#984ea3", "No Gene Drop" = "#999999",
+ "Drop Rasopathy" = "#e41a1c")
+
+drop_fig <- plot_roc_info(drop_genes_roc_df,
+ drop_genes_auroc_df,
+ drop_genes_colors)
+drop_gene_fig_file <- file.path(base_fig_folder, 'drop_gene_fig.pdf')
+ggplot2::ggsave(drop_gene_fig_file, plot = drop_fig, dpi = 600, width = 3,
+ height = 3)
+
+# Plot gene coefficients scatter
+ggplot(mut_v_copy_coef_df, aes(x = weight_mutation, y = weight,
+ color = weight_combined)) +
+ geom_point(alpha = 0.8, size = 0.1) +
+ scale_color_gradient2('Combined', low = "blue", mid = "grey", high = "red") +
+ xlab("Mutation Only - Gene Weight") +
+ ylab("Copy Number Only - Gene Weight") +
+ geom_text_repel(data = subset(mut_v_copy_coef_df,
+ (weight > 0.075 | weight < -0.015) |
+ (weight_mutation > 0.1 |
+ weight_mutation < -0.0625)),
+ arrow = arrow(length = unit(0.02, 'npc')),
+ segment.size = 0.3,
+ segment.alpha = 0.6,
+ box.padding = 0.17,
+ point.padding = 0.1,
+ size = 1.8,
+ fontface = 'italic',
+ aes(x = weight_mutation, y = weight, label = feature)) +
+ theme_bw() +
+ theme(axis.text = element_text(size = rel(0.5)),
+ axis.title = element_text(size = rel(0.6)),
+ axis.title.y = element_text(margin =
+ margin(t = 0, r = 0, b = 0, l = 0)),
+ axis.title.x = element_text(margin =
+ margin(t = 3, r = 0, b = 0, l = 0)),
+ legend.text = element_text(size = rel(0.4)),
+ legend.title = element_text(size = rel(0.5)),
+ legend.key = element_rect(size = 0.2),
+ legend.position = 'right',
+ legend.key.size = unit(0.4, 'lines'),
+ legend.margin = margin(l = -0.3, unit = 'cm'))
+
+mut_v_copy_weights_file <- file.path(base_fig_folder, 'mut_v_copy_weights.pdf')
+ggplot2::ggsave(mut_v_copy_weights_file, dpi = 600, width = 3, height = 2)
+
+# Set dropped genes that were removed to zero weight
+drop_genes_coef_df$weight[is.na(drop_genes_coef_df$weight)] <- 0
+drop_genes_coef_df$weight_combined[is.na(drop_genes_coef_df$weight_combined)] <- 0
+drop_genes_coef_df$weight_nodrop[is.na(drop_genes_coef_df$weight_nodrop)] <- 0
+
+ras_genes <- c('KRAS', 'HRAS', 'NRAS')
+
+ggplot(drop_genes_coef_df, aes(x = weight_nodrop, y = weight,
+ color = weight_combined)) +
+ geom_point(alpha = 0.8, size = 0.1) +
+ scale_color_gradient2('Drop Ras Genes', low = "blue", mid = "lightgrey",
+ high = "red") +
+ xlab("No Gene Drop - Gene Weight") +
+ ylab("Drop Rasopathy - Gene Weight") +
+ coord_fixed() +
+ geom_text_repel(data = subset(drop_genes_coef_df,
+ (weight_combined > 0.09 |
+ weight_combined < -0.06) |
+ feature %in% ras_genes),
+ arrow = arrow(length = unit(0.02, 'npc')),
+ segment.size = 0.3,
+ segment.alpha = 0.6,
+ size = 1.8,
+ fontface = 'italic',
+ aes(x = weight_nodrop, y = weight, label = feature)) +
+ theme_bw() +
+ theme(axis.text = element_text(size = rel(0.5)),
+ axis.title = element_text(size = rel(0.6)),
+ axis.title.y = element_text(margin =
+ margin(t = 0, r = 0, b = 0, l = 0)),
+ axis.title.x = element_text(margin =
+ margin(t = 3, r = 0, b = 0, l = 0)),
+ legend.text = element_text(size = rel(0.4)),
+ legend.title = element_text(size = rel(0.5)),
+ legend.key = element_rect(size = 0.2),
+ legend.position = 'right',
+ legend.key.size = unit(0.4, 'lines'),
+ legend.margin = margin(l = -0.3, unit = 'cm'))
+
+drop_rasopathy_weights_file <- file.path(base_fig_folder,
+ 'drop_rasopathy_weights.pdf')
+ggplot2::ggsave(drop_rasopathy_weights_file, dpi = 600, width = 3, height = 2)
diff --git a/scripts/viz/ras_differential_expression_figure.R b/scripts/viz/ras_differential_expression_figure.R
new file mode 100644
index 0000000..fcf4982
--- /dev/null
+++ b/scripts/viz/ras_differential_expression_figure.R
@@ -0,0 +1,52 @@
+# Gregory Way 2017
+# PanCancer Classifier
+# scripts/viz/ras_differential_expression_figure.R
+#
+# Visualize differences between differential expression analysis of Ras
+# wild-type to Ras mutant tumors and the machine learning derived gene
+# importance scores
+#
+# Usage: Run in command line
+#
+# Rscript --vanilla scripts/viz/ras_differential_expression_figure.R
+#
+# Output:
+# A single plot for supplementary figure S3
+
+library(ggplot2)
+library(ggpmisc)
+library(ggrepel)
+
+set.seed(123)
+
+# Load Data
+diff_exp_file <- file.path('classifiers', 'RAS', 'differential_expression.tsv')
+plot_df <- readr::read_tsv(diff_exp_file)
+
+color_logic <- (plot_df$weight > 0.05 | plot_df$weight < -0.05) |
+ (plot_df$stat > 28 | plot_df$stat < -16)
+
+ggplot(plot_df, aes(x = weight, y = stat)) +
+ geom_point(alpha = 0.5, color = ifelse(color_logic, 'red', 'grey50')) +
+ xlab('Ras Classifier Weight') +
+ geom_text_repel(data = subset(plot_df,
+ (weight > 0.05 | weight < -0.05) |
+ (stat > 28 | stat < -16)),
+ arrow = arrow(length = unit(0.01, 'npc')),
+ segment.size = 0.3,
+ segment.alpha = 0.6,
+ size = 1.5,
+ fontface = 'italic',
+ aes(x = weight, y = stat, label = gene)) +
+ ylab('Differential Expression Score') +
+ theme_bw() +
+ theme(axis.text = element_text(size = rel(0.65)),
+ axis.title = element_text(size = rel(0.8)),
+ axis.title.y = element_text(margin =
+ margin(t = 0, r = 0, b = 0, l = 0)),
+ axis.title.x = element_text(margin =
+ margin(t = 3, r = 0, b = 0, l = 0)),
+ plot.margin = margin(r = 0.3, l = 0.1, unit = 'cm'))
+
+diff_exp_fig <- file.path('classifiers', 'RAS', 'figures', 'diff_exprs.pdf')
+ggplot2::ggsave(diff_exp_fig, dpi = 600, width = 2.3, height = 2)
diff --git a/scripts/viz/ras_summary_figures.R b/scripts/viz/ras_summary_figures.R
index a2d678c..edd2c7c 100644
--- a/scripts/viz/ras_summary_figures.R
+++ b/scripts/viz/ras_summary_figures.R
@@ -16,12 +16,15 @@ checkpoint::checkpoint("2017-06-01", checkpointLocation = ".")
library(dplyr)
library(pheatmap)
library(ggplot2)
+library(ggrepel)
library(readr)
library(cowplot)
library(gridExtra)
library(Hmisc)
source(file.path("scripts", "util", "pancancer_util.R"))
+set.seed(123)
+
results_folder <- file.path("classifiers", "RAS")
results <- parse_summary(file.path(results_folder, "classifier_summary.txt"))
@@ -89,69 +92,53 @@ pheatmap(t(heat_ras_df * 100), scale = "none", cluster_rows = FALSE,
width = 8, height = 2)
# 2) Coefficients contributing to the model
-coef_plot_file <- file.path(results_folder, "figures", "ras_coef_plot.svg")
+coef_plot_file <- file.path(results_folder, "figures", "ras_coef_plot.pdf")
coef_df <- results[["Coefficients"]]
coef_df <- coef_df[, -1]
coef_df <- coef_df[order(coef_df$weight, decreasing = FALSE), ]
coef_df$rank <- 1:nrow(coef_df)
-p <- ggplot(coef_df, aes(x = 1:nrow(coef_df), y = weight)) +
- geom_point(fill = "black", size = 0.01) +
- base_theme + theme(axis.line.x = element_line(),
- axis.line.y = element_line(),
- axis.ticks = element_line(),
- axis.title = element_text(size = rel(1.5)),
- plot.margin = unit(c(0.25, 0.25, 0.1, 0.1), "cm")) +
- labs(list(x = "Rank", y = "Weight")) +
+color_logic <- (coef_df$weight > 0.09 | coef_df$weight < -0.06) |
+ (coef_df$feature == 'log10_mut')
+
+ggplot(coef_df, aes(x = 1:nrow(coef_df), y = weight)) +
+ geom_point(color = ifelse(color_logic, 'red', 'lightgrey'),
+ size = 0.01, alpha = 0.7) +
+ ylab('Ras Classifier Score') +
+ xlab('Rank') +
scale_y_continuous(breaks = seq(-0.25, 0.25, 0.05)) +
scale_x_continuous(breaks = seq(0, 8000, 2000)) +
geom_segment(aes(x = 0, y = 0, yend = 0, xend = nrow(coef_df)),
- colour = "red", linetype = "dashed", size = 0.2)
-
-p <- add_arrow_label(p = p, x = 1050, y = -0.205, label = "CDK13",
- offset = c(0, -0.003, -685, -.0002))
-p <- add_arrow_label(p = p, x = 1150, y = -0.190, label = "PDLIM4",
- offset = c(40, -0.003, -450, 0.006))
-p <- add_arrow_label(p = p, x = 1480, y = -0.172, label = "PDE5A",
- offset = c(80, -0.001, -690, .0003))
-p <- add_arrow_label(p = p, x = 1650, y = -0.155, label = "PURB",
- offset = c(80, -0.001, -680, -.00013))
-p <- add_arrow_label(p = p, x = 1700, y = -0.14, label = "FADS3",
- offset = c(80, -0.001, -710, .0002))
-p <- add_arrow_label(p = p, x = 2000, y = -0.12, label = "SEPP1",
- offset = c(90, -0.0001, -700, -.00015))
-p <- add_arrow_label(p = p, x = 1800, y = -0.1, label = "ALDOC",
- offset = c(80, 0, -750, -.0002))
-p <- add_arrow_label(p = p, x = 2100, y = -0.085, label = "PAPLN",
- offset = c(80, 0, -800, -.0002))
-p <- add_arrow_label(p = p, x = 1800, y = -0.07, label = "CLU",
- offset = c(80, 0, -550, -.0006))
-p <- add_arrow_label(p = p, x = 1900, y = -0.055, label = "CUL1",
- offset = c(80, 0, -690, -.0002))
-
-p <- add_arrow_label(p = p, x = 6800, y = 0.15, label = "PBX3",
- offset = c(-80, .0004, 600, -.004))
-p <- add_arrow_label(p = p, x = 6500, y = 0.12, label = "SPRY2",
- offset = c(-80, 0, 760, -.0006))
-p <- add_arrow_label(p = p, x = 6200, y = 0.1, label = "PPP1R3B",
- offset = c(-80, .0004, 1000, -.001))
-p <- add_arrow_label(p = p, x = 6050, y = 0.084, label = "C15orf52",
- offset = c(-80, .0004, 950, -.0015))
-p <- add_arrow_label(p = p, x = 5850, y = 0.062, label = "MLPH",
- offset = c(-80, .0004, 700, -.0015))
-p <- add_arrow_label(p = p, x = 5350, y = 0.041, label = "ERRFI1",
- offset = c(-80, .0004, 790, -.0015))
-p <- add_arrow_label(p = p, x = 6750, y = 0.032, label = "CMAS",
- offset = c(-80, -.001, 270, .01))
-
-p <- add_arrow_label(p = p, x = 6500, y = 0.016, label = "log10_mut",
- offset = c(-80, -0.0142, 900, 0.005))
-
-svg(coef_plot_file, width = 2.5, height = 2.25)
-p
-dev.off()
-
-# 3) Plot distributions of predictions according to variant classification
+ colour = "navy", linetype = "dashed", size = 0.2) +
+ geom_text_repel(data = subset(coef_df,
+ (weight > 0.09 | weight < -0.06) |
+ coef_df$feature == 'log10_mut'),
+ arrow = arrow(length = unit(0.01, 'npc')),
+ segment.size = 0.3,
+ segment.alpha = 0.3,
+ box.padding = 0.29,
+ point.padding = 0.3,
+ size = 1.3,
+ fontface = 'italic',
+ max.iter = 3e3,
+ force = 1,
+ direction = 'both',
+ xlim = c(0, 8000),
+ aes(x = rank, y = weight, label = feature)) +
+ base_theme +
+ theme(axis.line.x = element_line(),
+ axis.line.y = element_line(),
+ axis.ticks = element_line(),
+ axis.text = element_text(size = rel(0.55)),
+ axis.title = element_text(size = rel(0.65)),
+ plot.margin = unit(c(0.25, 0.25, 0.1, 0.1), "cm"),
+ axis.title.y = element_text(margin =
+ margin(t = 0, r = 0, b = 0, l = 0)),
+ axis.title.x = element_text(margin =
+ margin(t = 3, r = 0, b = 0, l = 0)))
+ggplot2::ggsave(coef_plot_file, dpi = 600, width = 1.7, height = 1.55)
+
+ # 3) Plot distributions of predictions according to variant classification
var_plot_file <- file.path(results_folder, "figures", "variant_fill_map.svg")
mut_df <- readr::read_tsv(file.path(results_folder, "tables",
"mutation_classification_scores.tsv"))