Exemple #1
0
def _drug_combination_heatmap(request, dataset, drug_id, cell_line_id,
                              template):
    if not all(isinstance(d, collections.Sequence) for d in drug_id):
        return HttpResponse(
            'Please select either one or more individual drugs, or a '
            'single drug combination',
            status=400)

    if len(drug_id) != 1:
        return HttpResponse(
            'Please select only one drug combination '
            'at a time', status=400)

    if len(cell_line_id) != 1:
        return HttpResponse(
            'Please select a single cell line for drug '
            'combination plots',
            status=400)

    color_by = request.GET.get('colorBy', 'off')
    if color_by != 'off':
        return HttpResponse(
            'Color overlay must be set to default for drug '
            'combination heat plots',
            status=400)

    response_metric = request.GET.get('drMetric', 'dip')
    if response_metric != 'dip':
        return HttpResponse(
            'Viability drug combination plots are not '
            'supported',
            status=400)

    dip_absolute = request.GET.get('drcType', 'rel') == 'abs'
    if dip_absolute:
        return HttpResponse(
            'Must use relative DIP rate for drug combination '
            'heat plots',
            status=400)

    try:
        ctrl_resp_data, expt_resp_data = df_dip_rates(
            dataset_id=dataset.id,
            drug_id=drug_id,
            cell_line_id=cell_line_id,
            use_dataset_names=True)
    except NoDataException:
        return HttpResponse(
            'No data found for this request. This '
            'drug/cell line/assay combination may not exist.',
            status=400)

    return plot_drug_combination_heatmap(ctrl_resp_data,
                                         expt_resp_data,
                                         template=template)
def _generate_dip_rates(dataset, regenerate_cache=False):
    file_name = 'dip_rates_{}.h5'.format(dataset.id)
    file_type = 'dip_rates'
    file_type_protocol = 1

    mod_date = timezone.now()
    file = _cached_file(dataset, file_type, file_type_protocol)

    if file and not regenerate_cache:
        full_path = file.file.name
    else:
        ctrl, expt = df_dip_rates(dataset, cell_line_id=None, drug_id=None)

        expt = expt.reset_index()
        n_drugs = expt['drug'].apply(len).max()
        expt = _unstack_doses(expt).reset_index()

        wells = Well.objects.filter(
            plate__dataset_id=dataset.id).select_related('plate')

        if ctrl is None:
            df_data = expt
        else:
            ctrl = ctrl.reset_index()
            df_data = pd.concat([ctrl, expt], ignore_index=True, sort=False)

        df_data.drop(columns=['plate', 'dataset'], inplace=True)

        well_df = pd.DataFrame(
            {
                'well_id': well.id,
                'plate': well.plate.name,
                'well_num': well.well_num,
                'well': well.plate.well_id_to_name(well.well_num)
            } for well in wells)

        df_data = df_data.merge(well_df, on='well_id')

        df_data.rename(
            columns={f'dose{n+1}': f'drug{n+1}.conc'
                     for n in range(n_drugs)},
            inplace=True)
        df_data.rename(columns={'cell_line': 'cell.line'}, inplace=True)

        df_data.sort_values(['plate', 'well_num'], inplace=True)

        columns = ['plate', 'well', 'cell.line'] + \
            [f'drug{n+1}' for n in range(n_drugs)] + \
            [f'drug{n+1}.conc' for n in range(n_drugs)] + \
            ['dip_rate', 'dip_fit_std_err']
        df_data = df_data[columns]

        full_path = os.path.join(settings.DOWNLOADS_ROOT, file_name)
        df_data.to_csv(full_path, sep='\t', index=False)
        df, created = HTSDatasetFile.objects.get_or_create(
            dataset=dataset,
            file_type=file_type,
            defaults={
                'file_type_protocol': file_type_protocol,
                'file': full_path
            })
        if not created:
            df.file_type_protocol = file_type_protocol
            df.file = full_path
            df.creation_date = mod_date
            df.save()

    return full_path
Exemple #3
0
def _dose_response_plot(request,
                        dataset,
                        dataset2_id,
                        permission_required,
                        drug_id,
                        cell_line_id,
                        plot_type,
                        template=default_plotly_template):
    if dataset2_id is not None:
        try:
            dataset2 = HTSDataset.objects.get(pk=dataset2_id)
        except HTSDataset.DoesNotExist:
            raise Http404()

        _assert_has_perm(request, dataset2, permission_required)

        if dataset.name == dataset2.name:
            return HttpResponse(
                'Cannot compare two datasets with the same '
                'name. Please rename one of the datasets.',
                status=400)

    datasets = dataset if not dataset2_id else [dataset, dataset2]

    color_by = request.GET.get('colorBy', 'off')
    if color_by == 'off':
        color_by = None

    drug_tag_ids = [int(dt) for dt in request.GET.getlist('dT')]
    color_groups = None
    aggregate_drugs = request.GET.get('aggregateDrugs', False) == "true"
    if not drug_id and drug_tag_ids:
        drug_id, drug_groups = _process_aggreate(
            request, 'drugs', drug_tag_ids, aggregate_drugs
            or color_by == 'dr', datasets)
        if aggregate_drugs:
            aggregate_drugs = drug_groups
        if color_by == 'dr':
            color_groups = drug_groups

    cell_line_tag_ids = [int(ct) for ct in request.GET.getlist('cT')]
    aggregate_cell_lines = request.GET.get('aggregateCellLines', False) \
                           == "true"
    if not cell_line_id and cell_line_tag_ids:
        cell_line_id, cell_line_groups = _process_aggreate(
            request, 'cell_lines', cell_line_tag_ids, aggregate_cell_lines
            or color_by == 'cl', datasets)
        if aggregate_cell_lines:
            aggregate_cell_lines = cell_line_groups
        if color_by == 'cl':
            color_groups = cell_line_groups

    if color_groups:
        color_groups = _make_tags_unique(color_groups)
    elif color_by == 'cl':
        # The tags will just be the cell lines themselves
        color_groups = {
            cl.name: [cl.name]
            for cl in CellLine.objects.filter(
                id__in=cell_line_id).order_by('name')
        }
    elif color_by == 'dr':
        # Ditto for drugs
        color_groups = {
            dr.name: [dr.name]
            for dr in Drug.objects.filter(id__in=drug_id).order_by('name')
        }

    if color_groups and len(color_groups) > MAX_COLOR_GROUPS:
        return HttpResponse(
            'Cannot plot using more than {} unique colors. Please remove '
            'some entries or turn off coloring to proceed.'.format(
                MAX_COLOR_GROUPS),
            status=400)

    if not cell_line_id:
        return HttpResponse('Please enter at least one cell line', status=400)
    if not drug_id:
        return HttpResponse('Please enter at least one drug', status=400)

    response_metric = request.GET.get('drMetric', 'dip')
    if response_metric not in ('dip', 'viability', 'compare'):
        return HttpResponse(
            'Unknown metric. Supported values: dip, '
            'viability, compare.',
            status=400)

    def _setup_dr_par(name, needs_toggle=False):
        if needs_toggle and \
                request.GET.get(name + 'Toggle', 'off') != 'on':
            return None
        par_name = request.GET.get(name, None)
        if par_name is not None and '_custom' in par_name:
            rep_value = request.GET.get(name + 'Custom', None)
            if int(rep_value) < 0:
                raise ValueError()
            par_name = par_name.replace('_custom', rep_value)
        return par_name

    try:
        dr_par = _setup_dr_par('drPar')
    except ValueError:
        return HttpResponse(
            'Parameter custom value '
            'needs to be a positive integer',
            status=400)

    try:
        dr_par_two = _setup_dr_par('drParTwo', needs_toggle=True)
    except ValueError:
        return HttpResponse(
            'Parameter two custom value '
            'needs to be a positive integer',
            status=400)

    try:
        dr_par_order = _setup_dr_par('drParOrder', needs_toggle=True)
    except ValueError:
        return HttpResponse(
            'Parameter order custom value '
            'needs to be a positive integer',
            status=400)

    # 'compare' plots are only available for one dataset and metric
    if response_metric == 'compare':
        if dataset2_id is not None:
            return HttpResponse(
                '"compare" mode not compatible with two '
                'datasets',
                status=400)
        if dr_par_two is not None:
            return HttpResponse(
                'Parameter two not available with "compare" '
                'mode',
                status=400)
        if dr_par_order is not None:
            return HttpResponse(
                'Parameter order not available with "compare" '
                'mode',
                status=400)
        if plot_type == 'drc':
            return HttpResponse(
                'Dose response curves not available with '
                '"compare" mode',
                status=400)

        if dr_par.endswith('_rel'):
            return HttpResponse(
                'Relative metrics are not available with '
                '"compare" mode',
                status=400)

    # Work out any non-standard parameters we need to calculate
    # e.g. non-standard IC concentrations
    ic_concentrations = set()
    ec_concentrations = set()
    e_values = set()
    e_rel_values = set()
    regexes = {
        IC_REGEX: ic_concentrations,
        EC_REGEX: ec_concentrations,
        E_REGEX: e_values,
        E_REL_REGEX: e_rel_values
    }
    need_aa = False
    need_hill = False
    need_emax = False
    need_einf = False
    for param_idx, param in enumerate((dr_par, dr_par_two, dr_par_order)):
        if param is None:
            continue
        if param == 'label' and param_idx == 2:
            continue
        if param == 'aa_obs':
            continue
        if param == 'aa':
            need_aa = True
            continue
        if param == 'hill':
            need_hill = True
            continue
        if param.startswith('emax'):
            need_emax = True
            continue
        if param == 'einf':
            need_einf = True
            continue
        for regex, value_list in regexes.items():
            match = regex.match(param)
            if not match:
                continue
            try:
                value = int(match.groups(0)[0])
                if value < 0 or value > 100:
                    raise ValueError()
                value_list.add(value)
                break
            except ValueError:
                return HttpResponse(
                    'Invalid custom value - must be '
                    'an integer between 1 and 100',
                    status=400)
        else:
            return HttpResponse('Unknown parameter: {}'.format(param),
                                status=400)

    dataset_ids = dataset.id if dataset2_id is None else [
        dataset.id, dataset2_id
    ]

    # Fit Hill curves and compute parameters
    if response_metric == 'compare':
        all_metrics = ('dip', 'viability')
    else:
        all_metrics = (response_metric, )

    try:
        base_params = [
            df_curve_fits(dataset_ids, metric, drug_id, cell_line_id)
            for metric in all_metrics
        ]
    except NoDataException:
        return HttpResponse(
            'No data found for this request. This drug/cell '
            'line/assay combination may not exist.',
            status=400)

    include_response_values = False

    ctrl_resp_data = None
    expt_resp_data = None
    if plot_type == 'drc':
        single_drug = len(
            base_params[0].index.get_level_values('drug').unique()) == 1
        single_cl = len(
            base_params[0].index.get_level_values('cell_line').unique()) \
                    == 1
        if single_cl and single_drug:
            try:
                if response_metric == 'dip':
                    ctrl_resp_data, expt_resp_data = df_dip_rates(
                        dataset_id=dataset_ids,
                        drug_id=drug_id,
                        cell_line_id=cell_line_id,
                        use_dataset_names=True)
                else:
                    expt_resp_data, ctrl_resp_data = _get_viability_scores(
                        datasets,
                        drug_id,
                        cell_line_id,
                        viability_time=base_params[0]._viability_time)
            except NoDataException:
                return HttpResponse(
                    'No data found for this request. This drug/'
                    'cell line/assay combination may not exist.',
                    status=400)
            include_response_values = True
            need_emax = True
            ic_concentrations = {50}
            ec_concentrations = {50}

    with warnings.catch_warnings(record=True) as w:
        fit_params = [
            fit_params_from_base(
                base_param_set,
                ctrl_resp_data=ctrl_resp_data,
                expt_resp_data=expt_resp_data,
                include_response_values=include_response_values,
                custom_ic_concentrations=ic_concentrations,
                custom_ec_concentrations=ec_concentrations,
                custom_e_values=e_values,
                include_aa=need_aa,
                include_hill=need_hill,
                include_emax=need_emax,
                include_einf=need_einf) for base_param_set in base_params
        ]
        # Currently only care about warnings if plotting AA
        if plot_type == 'drpar' and (dr_par == 'aa' or dr_par_two == 'aa'):
            w = [i for i in w if issubclass(i.category, AAFitWarning)]
            if w:
                return HttpResponse(w[0].message, status=400)

    if response_metric == 'compare':
        # Create new dataframe
        import pandas as pd
        fit_params = pd.concat([
            fit_params[0]['label'], fit_params[0][dr_par],
            fit_params[1][dr_par]
        ],
                               join='inner',
                               axis=1)
        fit_params.columns = [
            'label', 'dip__{}'.format(dr_par), 'viability__{}'.format(dr_par)
        ]
        fit_params._viability_time = base_params[1]._viability_time
        fit_params._drmetric = 'compare'
        dr_par, dr_par_two = fit_params.columns[1:]
    else:
        fit_params = fit_params[0]

    if plot_type == 'drpar':
        if dr_par is None:
            return HttpResponse('Dose response parameter is a required field',
                                status=400)

        try:
            plot_fig = plot_drc_params(
                fit_params,
                fit_param=dr_par,
                fit_param_compare=dr_par_two,
                fit_param_sort=dr_par_order,
                aggregate_cell_lines=aggregate_cell_lines,
                aggregate_drugs=aggregate_drugs,
                color_by=color_by,
                color_groups=color_groups,
                multi_dataset=dataset2_id is not None,
                template=template)
        except CannotPlotError as e:
            return HttpResponse(e, status=400)
    else:
        dip_absolute = request.GET.get('drcType', 'rel') == 'abs'
        plot_fig = plot_drc(fit_params,
                            is_absolute=dip_absolute,
                            color_by=color_by,
                            color_groups=color_groups,
                            template=template)

    return plot_fig