# ProMS: protein marker selection using proteomics or multiomics data


## Installation

To install `proms`, run:

```console
> pip install proms
```

or

```console
> pip install git+https://github.com/bzhanglab/proms
```


## Introduction

We provide two methods for selecting protein markers. 

### ProMS: Protein marker selection with proteomics data alone

The algorithm `ProMS` (Protein Marker Selection) works as follows. 
As a first step to remove uninformative features, `ProMS` examines each feature 
individually to determine the strength of the relationship between the feature 
and the target variable. A symmetric auroc score <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/6d4e730d903f26cecfe5284b224077c5.svg?invert_in_darkmode" align=middle width=62.042412299999995pt height=22.465723500000017pt/>
is defined to evaluate such strength: <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/cbd5eb655b77865cb6ac468c8b6daf0c.svg?invert_in_darkmode" align=middle width=201.58999079999998pt height=24.65753399999998pt/> 


`ProMS` only keeps the features with the top <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/42b7c0743f6690133133222f0462f5ca.svg?invert_in_darkmode" align=middle width=24.27516959999999pt height=24.65753399999998pt/> highest <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/6d4e730d903f26cecfe5284b224077c5.svg?invert_in_darkmode" align=middle width=62.042412299999995pt height=22.465723500000017pt/> 
scores. Here <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/c745b9b57c145ec5577b82542b2df546.svg?invert_in_darkmode" align=middle width=10.57650494999999pt height=14.15524440000002pt/> is a hyperparameter that needs to be tuned jointly 
with other hyperparameters of the final classifier. After the filtering step, data 
matrix <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/17104becada06c6cda0447c33ec6c846.svg?invert_in_darkmode" align=middle width=14.49764249999999pt height=22.55708729999998pt/> is reduced to <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/49a96e890ec991ba643ea6bcbd390091.svg?invert_in_darkmode" align=middle width=18.287603399999988pt height=24.7161288pt/> of size <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/a8e7abb5ab02a0c56e63b52bdea5917d.svg?invert_in_darkmode" align=middle width=42.018596399999986pt height=24.7161288pt/> where  <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/038e818044681e2d2b74874dd4909d3a.svg?invert_in_darkmode" align=middle width=46.72360934999999pt height=24.7161288pt/>.
To further reduce the redundancy among the remaining features,
`ProMS` groups <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/4ae3393b40dfbbbc0932cf55cbc55bc3.svg?invert_in_darkmode" align=middle width=12.060528149999989pt height=24.7161288pt/> features into <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/63bb9849783d01d91403bc9a5fea12a2.svg?invert_in_darkmode" align=middle width=9.075367949999992pt height=22.831056599999986pt/> clusters with weighted k-medoids clustering in sample space. 
The <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/63bb9849783d01d91403bc9a5fea12a2.svg?invert_in_darkmode" align=middle width=9.075367949999992pt height=22.831056599999986pt/> medoids from each cluster are selected as markers.
The whole process is illustrated in the following diagram:

<center><img src="https://github.com/bzhanglab/proms/blob/main/docs/proms.png" alt="proms" height="800"/></center>


### ProMS_mo: Protein marker selection with multi-omics data
We have <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/7b9a0316a2fcd7f01cfd556eedf72e96.svg?invert_in_darkmode" align=middle width=14.99998994999999pt height=22.465723500000017pt/> data sources, <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/47f62d38a583b29d95d9baba60567584.svg?invert_in_darkmode" align=middle width=76.32634349999998pt height=22.55708729999998pt/>, representing <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/7b9a0316a2fcd7f01cfd556eedf72e96.svg?invert_in_darkmode" align=middle width=14.99998994999999pt height=22.465723500000017pt/> 
different types of omics measurements that jointly depicts the same set of
samples <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/4ee1de2974f920dee2a9b2b40012e488.svg?invert_in_darkmode" align=middle width=59.221880849999984pt height=14.15524440000002pt/>. <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/4bc7c685b27caee1f740df61469bb5a3.svg?invert_in_darkmode" align=middle width=97.25457884999999pt height=24.65753399999998pt/> is a matrix of size  <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/bbef302f389d7bd8e7314d934215920d.svg?invert_in_darkmode" align=middle width=42.879533399999985pt height=19.1781018pt/> 
where rows correspond to samples and columns correspond to features in <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/77a3b857d53fb44e33b53e4c8b68351a.svg?invert_in_darkmode" align=middle width=5.663225699999989pt height=21.68300969999999pt/>th
data source. Without the loss of generality, we use <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/9fa8482e85abf0b11fd06a0c5848ff1c.svg?invert_in_darkmode" align=middle width=21.050188499999987pt height=22.55708729999998pt/> 
to represent the proteomics data from which we seek to select a set of informative 
markers that can be used to predict the target labels. Similar to `ProMS`, the first 
step of `ProMS_mo` involves filtering out insignificant features from each data source 
separately. Again we use <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/6d4e730d903f26cecfe5284b224077c5.svg?invert_in_darkmode" align=middle width=62.042412299999995pt height=22.465723500000017pt/>. `ProMS_mo` first applies the univariate filtering to target data source <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/9fa8482e85abf0b11fd06a0c5848ff1c.svg?invert_in_darkmode" align=middle width=21.050188499999987pt height=22.55708729999998pt/> 
and keeps only the top <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/42b7c0743f6690133133222f0462f5ca.svg?invert_in_darkmode" align=middle width=24.27516959999999pt height=24.65753399999998pt/> 
 features with the highest scores. We denote the minimal score among these remaining 
 features as <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/27e556cf3caa0673ac49a8f0de3c73ca.svg?invert_in_darkmode" align=middle width=8.17352744999999pt height=22.831056599999986pt/>. For other data source, `ProMS_mo` only keeps those features 
 with score larger than <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/27e556cf3caa0673ac49a8f0de3c73ca.svg?invert_in_darkmode" align=middle width=8.17352744999999pt height=22.831056599999986pt/>. Filtered data matrices are combined into a new 
 matrix <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/084c1922f77c103e97871103b78b4416.svg?invert_in_darkmode" align=middle width=18.287603399999988pt height=24.7161288pt/> of size <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/a8e7abb5ab02a0c56e63b52bdea5917d.svg?invert_in_darkmode" align=middle width=42.018596399999986pt height=24.7161288pt/>, where <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/e4a94d5a6c99ce374d7b1434b0021cb6.svg?invert_in_darkmode" align=middle width=89.92955564999998pt height=32.256008400000006pt/>
 and <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/35389bc6640e0f68bd4e60472c572da0.svg?invert_in_darkmode" align=middle width=12.92146679999999pt height=24.7161288pt/> is the number of features in the filtered data source <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/77a3b857d53fb44e33b53e4c8b68351a.svg?invert_in_darkmode" align=middle width=5.663225699999989pt height=21.68300969999999pt/>. Finally, 
 weighted k-medoids clustering is performed to partition the <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/4ae3393b40dfbbbc0932cf55cbc55bc3.svg?invert_in_darkmode" align=middle width=12.060528149999989pt height=24.7161288pt/> features into <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/63bb9849783d01d91403bc9a5fea12a2.svg?invert_in_darkmode" align=middle width=9.075367949999992pt height=22.831056599999986pt/>
 clusters in sample spaces. To guarantee that only protein markers are selected as 
 medoids, `ProMS_mo` first initializes the <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/63bb9849783d01d91403bc9a5fea12a2.svg?invert_in_darkmode" align=middle width=9.075367949999992pt height=22.831056599999986pt/>
 medoids to protein markers. During the iterative steps of optimization, 
 a medoid can only be replaced by another protein marker if such exchange improves the 
 objective function. After the iterative process converges, <img src="https://raw.githubusercontent.com/bzhanglab/proms/main/docs/svgs/63bb9849783d01d91403bc9a5fea12a2.svg?invert_in_darkmode" align=middle width=9.075367949999992pt height=22.831056599999986pt/>
 medoids are selected as the final protein markers for training a classifier.
The steps are depicted in the following diagram:

<center><img src="https://github.com/bzhanglab/proms/blob/main/docs/proms_mo.png" alt="proms" height="800"/></center>

## How to use the package

To evaluate ProMS algorithms, use `proms_train`. The required arguments are `-f run_config_file` 
and `-d data_config_file`. After cross validation, a final full model will be trained with 
all train data with hyperparameters that achieves the best average cross validation performance.

 To see all available arguments for the command:

```console
> proms_train -h
usage: proms_train [-h] -f FILE -d FILE [-s SEED] [-o OUTPUT_ROOT]
                   [-r RUN_VERSION]

optional arguments:
  -h, --help            show this help message and exit
  -f FILE, --file FILE  configuration file for the run
  -d FILE, --data FILE  configuration file for data set
  -s SEED, --seed SEED  random seed
  -o OUTPUT_ROOT, --output OUTPUT_ROOT
                        output directory
  -r RUN_VERSION, --run_version RUN_VERSION
                        name of the run, default to current date/time

```

`run_config_file` is a json file with the following schema:


| Key         | Description                                                                                                      | Type   |
|-------------|------------------------------------------------------------------------------------------------------------------|--------|
| repeat      | Number of cross validation repeats                                                                               | number |
| k           | Number of selected markers                                                                                       | array  |
| classifiers | One model will be trained for each classifier with selected markers as features                                  | array  |
| percentile  | Percent of features to keep in the filtering step, the algorithm will determine the best "percentile" to be used | array  |
| n_jobs      | maximum number of concurrently running workers                                                                   | number |

<br/><br/>

`data_config_file` is a json file with the following schema:

| Key                     | Description                                                       | Type   |
|-------------------------|-------------------------------------------------------------------|--------|
| name                    | name of the dataset                                               | string |
| data_root               | root directory of data folder relative to the config file         | string |
| train_dataset           | path of train dataset relative to data_root                       | string |
| test_dataset            | path of test dataset relative to data_root (optional)             | string |
| target_view             | name of data view where markers will be selected                  | string |
| target_label            | name of label to predict                                          | string |
| data                    | description of actual data                                        | object |
| data: train              | object describing train data                                      | object |
| data: train : label        | object describing class label information                         | object |
| data: train : label : file   | name of file with class label information                         | string |
| data: train : view         | a list of objects describing training omics data type             | array  |
| data: train : view[i] :type | name of omics data type i                                         | string |
| data: train : view[i] :file | name of file for omics data type i                                | string |
| data: test               | object describing test data (optional)                            | object |
| data: test : label         | pbject describing class label information (optional)              | object |
| data: test : label :file    | name of file with class label information (optional)              | string |
| data: test : view          | array of single object describing test omics data type (optional) | array  |
| data: test : view[0] :type  | name of target omics data type (optional, must match target_view) | string |
| data: test : view[0] :file  | name of file for target omics data type                           | string |

<br/><br/>

To make predictions on new dataset with the full model, run 

```console
proms_predict -m /path/to/saved/full_model.pkl -d predict_data_config
```

`predict_data_config` is a json file with the following schema:


| Key                     | Meaning                                                           | Type   |
|-------------------------|-------------------------------------------------------------------|--------|
| name                    | name of the dataset                                               | string |
| data_root               | root directory of data folder relative to the config file         | string |
| predict_dataset         | path of predict dataset relative to data_root                     | string |
| data                    | description of actual data                                        | object |
| data: predict            | object describing predict data                                    | object |
| data: predict : view       | array of single object describing preidction omics data type      | array  |
| data: test : view[0] : type  | name of target omics data type (must match training target_view)  | string |
| data: test : view[0] : file  | name of file for prediction omics data type                       | string |

<br/><br/>


### Example: 

<br/>

To test the package, download the dataset from [here](https://zhanglab.s3-us-west-2.amazonaws.com/proms_test.tgz). 


```console
tar xvfz proms_test.tgz
cd proms_test
proms_train -f ./test/run_config.json -d ./data/crc.json 2>proms.log
```
Here we redirect all outputs other than stdout to a log file.

The cross validation results will be created as:
`[TIME_STAMP]` is the time stamp when the program started. By default, this 
is used to distinguish different runs.

`results/[TIME_STAMP]/crc_results_[TIME_STAMP]_eval.tsv`

The prediction results on test dataset with full model will be created as:

`results/[TIME_STAMP]/crc_results_[TIME_STAMP]_full.tsv`

The full model is saved as 

`results/[TIME_STAMP]/full_model/full_model.pkl`

To make prediction on another dataset with trained full model. (Here, we again
make prediction on the test dataset.)


Note: replace [TIME_STAMP] with actual value.
```console
proms_predict -m results/[TIME_STAMP]/full_model/full_model.pkl -d data/crc_predict.json
```

 

