.. _reason_codes:

============
Reason Codes
============

To compute reason codes you need to have :ref:`feature impact<feature_impact-label>` computed for a
model, and :doc:`predictions</entities/predict_job>` for an uploaded dataset computed with a
selected model.

Computing reason codes is a resource-intensive task, but you can configure it with maximum codes,
and prediction value thresholds to speed up the process.

Quick Reference
***************

.. code-block:: python

    import datarobot as dr
    # Get project
    my_projects = dr.Project.list()
    project = my_projects[0]
    # Get model
    models = project.get_models()
    model = models[0]
    # Compute feature impact
    impact_job = model.request_feature_impact()
    impact_job.wait_for_completion()
    # Upload dataset
    dataset = project.upload_dataset('./data_to_predict.csv')
    # Compute predictions
    predict_job = model.request_predictions(dataset.id)
    predict_job.wait_for_completion()
    # Initialize reason codes
    rci_job = dr.ReasonCodesInitialization.create(project.id, model.id)
    rci_job.wait_for_completion()
    # Compute reason codes with default parameters
    rc_job = dr.ReasonCodes.create(project.id, model.id, dataset.id)
    rc = rc_job.get_result_when_complete()
    # Iterate through predictions with reason codes
    for row in rc.get_rows():
        print row.prediction
        print row.reason_codes
    # download to a CSV file
    rc.download_to_csv('reason_codes.csv')

List Reason Codes
*****************
You can use the ``ReasonCodes.list()`` method to return a list of reason codes computed for
a project's models:

.. code-block:: python

    import datarobot as dr
    reason_codes = dr.ReasonCodes.list('58591727100d2b57196701b3')
    print(reason_codes)
    >>> [ReasonCodes(id=585967e7100d2b6afc93b13b,
                     project_id=58591727100d2b57196701b3,
                     model_id=585932c5100d2b7c298b8acf),
         ReasonCodes(id=58596bc2100d2b639329eae4,
                     project_id=58591727100d2b57196701b3,
                     model_id=585932c5100d2b7c298b8ac5),
         ReasonCodes(id=58763db4100d2b66759cc187,
                     project_id=58591727100d2b57196701b3,
                     model_id=585932c5100d2b7c298b8ac5),
         ...]
    rc = reason_codes[0]

    rc.project_id
    >>> u'58591727100d2b57196701b3'
    rc.model_id
    >>> u'585932c5100d2b7c298b8acf'

You can pass following parameters to filter the result:

* ``model_id`` -- str, used to filter returned reason codes by model_id.
* ``limit`` -- int, limit for number of items returned, default: no limit.
* ``offset`` -- int, number of items to skip, default: 0.

**List Reason Codes Example:**

.. code-block:: python

    dr.ReasonCodes.list('pid', model_id='model_id', limit=20, offset=100)


Initialize Reason Codes
***********************
In order to compute reason codes you have to initialize it for a particular model.

.. code-block:: python

    dr.ReasonCodesInitialization.create(project_id, model_id)

Compute Reason Codes
********************
If all prerequisites are in place, you can compute reason codes in the following way:

.. code-block:: python

    import datarobot as dr
    project_id = '5506fcd38bd88f5953219da0'
    model_id = '5506fcd98bd88f1641a720a3'
    dataset_id = '5506fcd98bd88a8142b725c8'
    rc_job = dr.ReasonCodes.create(project_id, model_id, dataset_id,
                                   max_codes=2, threshold_low=0.2, threshold_high=0.8)
    rc = rc_job.get_result_when_complete()

Where:

* ``max_codes`` the maximum number of reason codes to compute for each row.
* ``threshold_low`` and ``threshold_high`` are thresholds for the value of the prediction of the
  row, reason codes will be computed for a row if the row's prediction value is higher than
  ``threshold_high`` or lower than ``threshold_low``. If no thresholds are specified, reason codes
  will be computed for all rows.

Retrieving Reason Codes
***********************
You have three options for retrieving reason codes.

.. note:: ``ReasonCodes.get_all_as_dataframe()`` and ``ReasonCodes.download_to_csv()`` reformat
          reason codes to match the schema of CSV file downloaded from UI (RowId, Prediction,
          Reason 1 Strength, Reason 1 Feature, Reason 1 Value, ..., Reason N Strength,
          Reason N Feature, Reason N Value)

Get reason codes rows one by one as :class:`dr.models.reason_codes.ReasonCodesRow` objects:

.. code-block:: python

    import datarobot as dr
    project_id = '5506fcd38bd88f5953219da0'
    reason_codes_id = '5506fcd98bd88f1641a720a3'
    rc = dr.ReasonCodes.get(project_id, reason_codes_id)
    for row in rc.get_rows():
        print row.reason_codes

Get all rows as ``pandas.DataFrame``:

.. code-block:: python

    import datarobot as dr
    project_id = '5506fcd38bd88f5953219da0'
    reason_codes_id = '5506fcd98bd88f1641a720a3'
    rc = dr.ReasonCodes.get(project_id, reason_codes_id)
    reason_codes_df = rc.get_all_as_dataframe()

Download all rows to a file as CSV document:

.. code-block:: python

    import datarobot as dr
    project_id = '5506fcd38bd88f5953219da0'
    reason_codes_id = '5506fcd98bd88f1641a720a3'
    rc = dr.ReasonCodes.get(project_id, reason_codes_id)
    rc.download_to_csv('reason_codes.csv')
