A metric is a quantitative measure used to evaluate the performance of the different methods in solving the specific task problem.
This guide will show you how to create a new Viash component. In the following we will show examples for both Python and R. Note that the Label Projection task is used throughout the guide, so make sure to replace any occurrences of "label_projection" with your task of interest.
[notice] Checking if Docker image is available at 'ghcr.io/openproblems-bio/common/create_component:dev'
[warning] Could not pull from 'ghcr.io/openproblems-bio/common/create_component:dev'. Docker image doesn't exist or is not accessible.
[notice] Building container 'ghcr.io/openproblems-bio/common/create_component:dev' with Dockerfile
This will create a new folder at src/tasks/label_projection/metrics/my_metric_py containing a Viash config and a script.
src/tasks/label_projection/metric/my_metric_py
├── script.py Script for running the metric.
├── config.vsh.yaml Config file for metric.
└── ... Optional additional resources.
viash run src/common/create_component/config.vsh.yaml --\--task label_projection \--type metric \--name my_metric_r \--language r
This will create a new folder at src/tasks/label_projection/metrics/my_metric_r containing a Viash config and a script.
src/tasks/label_projection/metrics/my_metric_r
├── script.R Script for running the metric.
├── config.vsh.yaml Config file for metric.
└── ... Optional additional resources.
Tip
Some tasks have multiple metric subtypes (e.g. batch_integration), which will require you to use a different value for --type corresponding to the desired metric subtype.
Change the --name to a unique name for your metric. It must match the regex [a-z][a-z0-9_]* (snakecase).
A config file contains metadata of the component and the dependencies required to run it. In steps 2 and 3 we will fill in the required information.
A script contains the code to run the metric. In step 4 we will edit the script.
Step 2: Fill in metadata
The Viash config contains metadata of your metric, which script is used to run it, and the required dependencies.
Generated config file
This is what the config.vsh.yaml generated by the create_component component looks like:
# The API specifies which type of component this is.# It contains specifications for:# - The input/output files# - Common parameters# - A unit test__merge__: ../../api/comp_metric.yamlfunctionality: # A unique identifier for your component (required). # Can contain only lowercase letters or underscores.name: my_metric_py # Metadata for your componentinfo:metrics: # A unique identifier for your metric (required). # Can contain only lowercase letters or underscores.name: my_metric_py # A relatively short label, used when rendering visualisarions (required)label: My Metric Py # A one sentence summary of how this metric works (required). Used when # rendering summary tables.summary:"FILL IN: A one sentence summary of this metric." # A multi-line description of how this component works (required). Used # when rendering reference documentation. description: | FILL IN: A (multi-line) description of how this metric works. # A reference key from the bibtex library at src/common/library.bib (required).reference: bibtex_reference_key # URL to the documentation for this metric (required).documentation_url: https://url.to/the/documentation # URL to the code repository for this metric (required).repository_url: https://github.com/organisation/repository # The minimum possible value for this metric (required)min:0 # The maximum possible value for this metric (required)max:1 # Whether a higher value represents a 'better' solution (required)maximize:true # Component-specific parameters (optional) # arguments: # - name: "--n_neighbors" # type: "integer" # default: 5 # description: Number of neighbors to use. # Resources required to run the componentresources: # The script of your component (required)-type: python_scriptpath: script.py # Additional resources your script needs (optional) # - type: file # path: weights.ptplatforms: # Specifications for the Docker image for this component.-type: dockerimage: ghcr.io/openproblems-bio/base_python:1.0.1 # Add custom dependencies here (optional). For more information, see # https://viash.io/reference/config/platforms/docker/#setup . # setup: # - type: python # packages: scib==1.1.3 # This platform allows running the component natively-type: native # Allows turning the component into a Nextflow module / pipeline.-type: nextflowdirectives:label:[midmem, midcpu]
Contents of config.vsh.yaml
# The API specifies which type of component this is.# It contains specifications for:# - The input/output files# - Common parameters# - A unit test__merge__: ../../api/comp_metric.yamlfunctionality: # A unique identifier for your component (required). # Can contain only lowercase letters or underscores.name: my_metric_r # Metadata for your componentinfo:metrics: # A unique identifier for your metric (required). # Can contain only lowercase letters or underscores.name: my_metric_r # A relatively short label, used when rendering visualisarions (required)label: My Metric R # A one sentence summary of how this metric works (required). Used when # rendering summary tables.summary:"FILL IN: A one sentence summary of this metric." # A multi-line description of how this component works (required). Used # when rendering reference documentation. description: | FILL IN: A (multi-line) description of how this metric works. # A reference key from the bibtex library at src/common/library.bib (required).reference: bibtex_reference_key # URL to the documentation for this metric (required).documentation_url: https://url.to/the/documentation # URL to the code repository for this metric (required).repository_url: https://github.com/organisation/repository # The minimum possible value for this metric (required)min:0 # The maximum possible value for this metric (required)max:1 # Whether a higher value represents a 'better' solution (required)maximize:true # Component-specific parameters (optional) # arguments: # - name: "--n_neighbors" # type: "integer" # default: 5 # description: Number of neighbors to use. # Resources required to run the componentresources: # The script of your component (required)-type: r_scriptpath: script.R # Additional resources your script needs (optional) # - type: file # path: weights.ptplatforms: # Specifications for the Docker image for this component.-type: dockerimage: ghcr.io/openproblems-bio/base_r:1.0.1 # Add custom dependencies here (optional). For more information, see # https://viash.io/reference/config/platforms/docker/#setup . # setup: # - type: r # packages: tidyverse # This platform allows running the component natively-type: native # Allows turning the component into a Nextflow module / pipeline.-type: nextflowdirectives:label:[midmem, midcpu]
Required metadata fields
Please make sure that the following fields in the functionality and functionality.info sections in the config file are filled in. The metrics component can contain several metric values these are listed in the functionality.info.metrics.
functionality.name
A unique identifier for the metric component. Must be written in snake case. Example: my_new_metric. This will be the same as the name given in the --name argument of the create_component command above.
functionality.info.metrics[]
.name: A unique identifier for the metric (if only 1 metric in the component can be the same as functionality.name). Must be written in snake case. Example: my_new_metric.
.pretty_name: A label for the metric used for visualisations and documentation. Example: "My new metric".
.summary: A one sentence summary of the metric. Used for creating short overviews of the components in a task.
.description: An explanation for how the metric works. Used for creating reference documentation of a task.
.reference: A bibtex reference key to the paper where the metrics is used.
.documentation_url: The url to the documentation of the metrics used.
.repository_url: The repository url for the metrics used.
.min: The minimum value of the metrics (-inf if there isn’t a min).
.max: The maximum value of the metrics (+inf if there isn’t a max).
.maximize: Set to true if a higher value is better and false if a lower value is better.
__merge__
The file specified in this field contains information regarding the input and output arguments of the component, as well as a unit test to ensure that the component is functioning properly. Normally you don’t need to change this if you gave the right arguments to the create_component component.
Step 3: Add dependencies
Each component has it’s own set of dependencies, because different components might have conflicting dependencies.
For your convenience we have created 2 base images that can be used for python or R scripts. These images can be found in the OpenProblems github repo base-images. Click on the packages to view the url you need to use. You are not required to use these images but make sure the required packages are installed to make sure OpenProblems works properly.
Update the setup definition in the platforms section of the config file. This section describes the packages that need to be installed in the Docker image and are required for your method to run.
If you’re using a custom image use the following minimum setup:
import anndata as ad## VIASH START# Note: this section is auto-generated by viash at runtime. To edit it, make changes# in config.vsh.yaml and then run `viash config inject config.vsh.yaml`.par = {'input_solution': 'resources_test/label_projection/pancreas/solution.h5ad','input_prediction': 'resources_test/label_projection/pancreas/prediction.h5ad','output': 'output.h5ad'}meta = {'functionality_name': 'my_metric_py'}## VIASH ENDprint('Reading input files', flush=True)input_solution = ad.read_h5ad(par['input_solution'])input_prediction = ad.read_h5ad(par['input_prediction'])print('Compute metrics', flush=True)# metric_ids and metric_values can have length > 1# but should be of equal lengthuns_metric_ids = [ 'my_metric_py' ]uns_metric_values = [ 0.5 ]print("Write output AnnData to file", flush=True)output = ad.AnnData( uns={'dataset_id': input_prediction.uns['dataset_id'],'normalization_id': input_prediction.uns['normalization_id'],'method_id': input_prediction.uns['method_id'],'metric_ids': uns_metric_ids,'metric_values': uns_metric_values })output.write_h5ad(par['output'], compression='gzip')
Contents of script.R
library(anndata)## VIASH STARTpar <-list(input_solution ="resources_test/label_projection/pancreas/solution.h5ad",input_prediction ="resources_test/label_projection/pancreas/prediction.h5ad",output ="output.h5ad")meta <-list(functionality_name ="my_metric_r")## VIASH ENDcat("Reading input files\n")input_solution <- anndata::read_h5ad(par[["input_solution"]])input_prediction <- anndata::read_h5ad(par[["input_prediction"]])cat("Compute metrics\n")# metric_ids and metric_values can have length > 1# but should be of equal lengthuns_metric_ids <-c("my_metric_r")uns_metric_values <-c(0.5)cat("Write output AnnData to file\n")output <- anndata::AnnData(uns =list(dataset_id = input_prediction$uns[["dataset_id"]],normalization_id = input_prediction$uns[["normalization_id"]],method_id = input_prediction$uns[["method_id"]],metric_ids = uns_metric_ids,metric_values = uns_metric_values ))output$write_h5ad(par[["output"]], compression ="gzip")
Required sections
Imports and libraries
In the top section of the script you can define which packages/libraries the metric needs. If you add a new or different package add the dependency to config.vsh.yaml in the setup field (see above).
Argument block
The Viash code block is designed to facilitate prototyping, by enabling you to execute directly by running python script.py (or Rscript script.R for R users). Note that anything between “VIASH START” and “VIASH END” will be removed and replaced with a CLI argument parser when the components are being built by Viash.
Here, the par dictionary contains all the arguments defined in the config.vsh.yaml file (including those from the defined __merge__ file). When adding a argument in the par dict also add it to the config.vsh.yaml in the arguments section.
Read input data
This section reads any input AnnData files passed to the component.
Generate results
This is the most important section of your script, as it defines the core functionality provided by the component. It processes the input data to create results for the particular task at hand.
Write output data to file
The output stored in a AnnData object and then written to an .h5ad file. The format is specified by the API file specified in the __merge__ field in the config file.
Step 5: Try component
Your component’s API file contains the necessary unit tests to check whether your component works and the output is in the correct format.
You can test your component by using the following command:
viash test src/tasks/label_projection/metrics/my_metric_py/config.vsh.yaml
Output
Running tests in temporary directory: '/tmp/viash_test_f18125333023148501821'
====================================================================
+/tmp/viash_test_f18125333023148501821/build_executable/f1 ---verbosity 6 ---setup cachedbuild
[notice] Building container 'ghcr.io/openproblems-bio/label_projection/metrics/f1:test' with Dockerfile
[info] Running 'docker build -t ghcr.io/openproblems-bio/label_projection/metrics/f1:test /tmp/viash_test_f18125333023148501821/build_executable -f /tmp/viash_test_f18125333023148501821/build_executable/tmp/dockerbuild-f1-PdO4wh/Dockerfile'
#0 building with "default" instance using docker driver
#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile: 576B done
#1 DONE 0.0s
#2 [internal] load .dockerignore
#2 transferring context: 2B done
#2 DONE 0.0s
#3 [internal] load metadata for ghcr.io/openproblems-bio/base_python:1.0.1
#3 DONE 0.1s
#4 [1/2] FROM ghcr.io/openproblems-bio/base_python:1.0.1@sha256:54ab73ef47dee4b0ba32bcacab08b760351d118943e4b30d7476f9a85072303d
#4 CACHED
#5 [2/2] RUN pip install --upgrade pip && pip install --upgrade --no-cache-dir "scikit-learn"
#5 0.610 Requirement already satisfied: pip in /usr/local/lib/python3.10/site-packages (23.2.1)
#5 1.233 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
#5 1.845 Requirement already satisfied: scikit-learn in /usr/local/lib/python3.10/site-packages (1.3.0)
#5 2.132 Requirement already satisfied: numpy>=1.17.3 in /usr/local/lib/python3.10/site-packages (from scikit-learn) (1.24.4)
#5 2.133 Requirement already satisfied: scipy>=1.5.0 in /usr/local/lib/python3.10/site-packages (from scikit-learn) (1.11.2)
#5 2.134 Requirement already satisfied: joblib>=1.1.1 in /usr/local/lib/python3.10/site-packages (from scikit-learn) (1.3.2)
#5 2.134 Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.10/site-packages (from scikit-learn) (3.2.0)
#5 2.577 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
#5 DONE 2.8s
#6 exporting to image
#6 exporting layers
#6 exporting layers 1.6s done
#6 writing image sha256:e92bb24c6307d203adee24d9fea735a1a1eb89e9262dbbdcc6088afb6bbd630e
#6 writing image sha256:e92bb24c6307d203adee24d9fea735a1a1eb89e9262dbbdcc6088afb6bbd630e done
#6 naming to ghcr.io/openproblems-bio/label_projection/metrics/f1:test done
#6 DONE 1.6s
====================================================================
+/tmp/viash_test_f18125333023148501821/test_check_metric_config/test_executable
Load config data
check general fields
Check info fields
{'metrics': [{'name': 'f1_weighted', 'label': 'F1 weighted', 'summary': 'Average weigthed support between each labels F1 score', 'description': "Calculates the F1 score for each label, and find their average weighted by support (the number of true instances for each label). This alters 'macro' to account for label imbalance; it can result in an F-score that is not between precision and recall.", 'reference': 'grandini2020metrics', 'min': 0, 'max': 1, 'maximize': True, 'v1': {'path': 'openproblems/tasks/label_projection/metrics/f1.py', 'commit': 'bb16ca05ae1ce20ce59bfa7a879641b9300df6b0'}}, {'name': 'f1_macro', 'label': 'F1 macro', 'summary': 'Unweighted mean of each label F1-score', 'description': 'Calculates the F1 score for each label, and find their unweighted mean. This does not take label imbalance into account.', 'reference': 'grandini2020metrics', 'min': 0, 'max': 1, 'maximize': True, 'v1': {'path': 'openproblems/tasks/label_projection/metrics/f1.py', 'commit': 'bb16ca05ae1ce20ce59bfa7a879641b9300df6b0'}}, {'name': 'f1_micro', 'label': 'F1 micro', 'summary': 'Calculation of TP, FN and FP.', 'description': 'Calculates the F1 score globally by counting the total true positives, false negatives and false positives.', 'reference': 'grandini2020metrics', 'min': 0, 'max': 1, 'maximize': True, 'v1': {'path': 'openproblems/tasks/label_projection/metrics/f1.py', 'commit': 'bb16ca05ae1ce20ce59bfa7a879641b9300df6b0'}}], 'type': 'metric', 'type_info': {'label': 'Metric', 'summary': 'A label projection metric.', 'description': 'A metric for evaluating predicted labels.\n'}}
All checks succeeded!
====================================================================
+/tmp/viash_test_f18125333023148501821/test_run_and_check_adata/test_executable
>> Checking whether input files exist
>> Running script as test
Load data
Encode labels
Compute F1 score
Store metric value
Writing adata to file
>> Checking whether output file exists
>> Reading h5ad files and checking formats
Reading and checking input_solution
AnnData object with n_obs × n_vars = 100 × 500
obs: 'label', 'batch'
var: 'hvg', 'hvg_score'
uns: 'dataset_id', 'normalization_id'
obsm: 'X_pca'
layers: 'counts', 'normalized'
Reading and checking input_prediction
AnnData object with n_obs × n_vars = 100 × 500
obs: 'batch', 'label_pred'
var: 'hvg', 'hvg_score'
uns: 'dataset_id', 'method_id', 'normalization_id'
obsm: 'X_pca'
layers: 'counts', 'normalized'
Reading and checking output
AnnData object with n_obs × n_vars = 100 × 500
obs: 'batch', 'label_pred'
var: 'hvg', 'hvg_score'
uns: 'dataset_id', 'method_id', 'metric_ids', 'metric_values', 'normalization_id'
obsm: 'X_pca'
layers: 'counts', 'normalized'
All checks succeeded!
====================================================================
[32mSUCCESS! All 2 out of 2 test scripts succeeded![0m
Cleaning up temporary directory
Visit “Run tests” for more information on running unit tests and how to interpret common error messages.
You can also run your component on local files using the viash run command. For example:
viash run src/tasks/label_projection/metrics/my_metric_py/config.vsh.yaml --\--input_prediction resources_test/label_projection/pancreas/knn.h5ad \--input_solution resources_test/label_projection/pancreas/solution.h5ad \--output output.h5ad