def test_split_overflowing_features():
    features = [
        GraphicFeature(start=10, end=20, strand=+1, label="a"),
        GraphicFeature(start=40, end=55, strand=+1, label="b"),
        GraphicFeature(start=-20, end=2, strand=+1, label="c"),
    ]

    # PLOT AND EXPORT A LINEAR VIEW OF THE CONSTRUCT
    record = GraphicRecord(sequence_length=50, features=features)
    record.split_overflowing_features_circularly()
    new_features_locations_and_labels = sorted([(f.start, f.end, f.label)
                                                for f in record.features])
    assert new_features_locations_and_labels == [
        (0, 2, "c"),
        (0, 5, "b"),
        (10, 20, "a"),
        (30, 49, "c"),
        (40, 49, "b"),
    ]
Ejemplo n.º 2
0
    def plot(self,
             ax=None,
             plot_coverage=True,
             plot_reference=False,
             reference_ax=None,
             figsize="auto",
             features_filters=(),
             features_properties=None,
             reference_reads_shares="auto"):
        """Plot the sequencing matches.

        Useful to get a general overview of the sequencing (coverage, mutations
        etc.)

        Parameters
        ----------

        ax
          Matplotlib ax on which to plot the alignments. If None, one will be
          automatically created.

        plot_coverage
          If True, the plots will display in the background a filled blue line
          indicating how many times each nucleotide of the sequence is covered
          by the succesfull alignments.

        plot_reference
          If True, a schema of the reference record will be plotted, by default
          above the reads plot.

        reference_ax
          If provided and plot_reference is True, the reference record will be
          plotted on this ax.

        figsize
          Size of the final figure. Leave it to 'auto' for a figure of width
          12 and automatically chosen height. Or e.g. (16, 'auto') for a figure
          of width 12 and automatically chosen height

        features_filters
          List of functions (feature=>True/False). Features for which at least
          one test is False will not appear in the reference record plot.

        features_properties
          DNA Features Viewer property functions that can be used to change
          the appearance of the reference record.

        reference_reads_shares
          Relative shares of the pictures that should be occupied by the
          reference and by the reads. It is an experimental parameter so
          leave it to 'auto' for now.

        """
        class AnnotationsGraphicTranslator(BiopythonTranslator):
            def compute_feature_color(self, f):
                return "#f9d277"

            def compute_feature_label(self, f):
                return BiopythonTranslator.compute_feature_label(f)[:20]

            def compute_filtered_features(self, features):
                def is_not_parameter(f):
                    label = "".join(f.qualifiers.get('label', ''))
                    return label not in ('cover', 'no_primer')

                return [f for f in features if is_not_parameter(f)]

        if plot_reference:
            translator = AnnotationsGraphicTranslator(
                features_filters=features_filters,
                features_properties=features_properties)
            grecord = translator.translate_record(self.reference)
            if not self.linear:
                grecord.split_overflowing_features_circularly()

        if figsize == "auto":
            figsize = (12, "auto")
        if figsize[1] == "auto":
            sequencing_ax_height = 2 + 0.35 * len(self.read_reference_matches)
            if not plot_reference:
                figure_height = sequencing_ax_height
            else:
                ref_ax, _ = grecord.plot(with_ruler=False,
                                         figure_width=figsize[0])
                ref_fig_height = ref_ax.figure.get_size_inches()[1]
                figure_height = sequencing_ax_height + ref_fig_height
                if reference_reads_shares == "auto":
                    reference_reads_shares = (int(100 * ref_fig_height),
                                              int(100 * sequencing_ax_height))
                plt.close(ref_ax.figure)
            figsize = (figsize[0], figure_height)
        elif reference_reads_shares == "auto":
            reference_reads_shares = (1, 2)

        if plot_reference:
            if reference_ax is None:
                gs = gridspec.GridSpec(sum(reference_reads_shares), 1)
                fig = plt.figure(figsize=figsize, facecolor="w")
                reference_ax = fig.add_subplot(gs[:reference_reads_shares[0]])
                ax = fig.add_subplot(gs[reference_reads_shares[0]:])

            grecord.plot(reference_ax, with_ruler=False, annotate_inline=True)
            self.plot(ax=ax, plot_coverage=plot_coverage, plot_reference=False)
            ax.set_xlim(reference_ax.get_xlim())
            return ax

        # so the first read in the list gets displayed on top
        read_reference_matches = OrderedDict(
            [item for item in list(self.read_reference_matches.items())[::-1]])
        L = len(self.reference)
        if ax is None:
            fig, ax = plt.subplots(1, figsize=figsize)
        ax.set_xlim(-2, L)
        ax.set_ylim(0, len(read_reference_matches) + 2)
        ax.set_yticks(range(1, len(read_reference_matches) + 1))
        ax.set_yticklabels([name for name in read_reference_matches])
        ax.spines['right'].set_visible(False)
        ax.spines['top'].set_visible(False)
        ax.yaxis.set_ticks_position('left')
        ax.xaxis.set_ticks_position('bottom')

        gr_record = GraphicRecord(sequence_length=L, features=[])

        for i, (read_name,
                matches) in enumerate(read_reference_matches.items()):
            y = i + 1
            ax.axhline(y, ls=":", lw=0.5, color="#aaaaaa", zorder=-1000)
            if matches.primer.metadata.get('available', False):
                color = '#f7a3f6'
            else:
                color = "#a3c3f7"
            for match in matches.read_matches:
                gr_record.features = [
                    GraphicFeature(start=match.start,
                                   end=match.end,
                                   strand=match.strand,
                                   color=color)
                ]
                gr_record.split_overflowing_features_circularly()
                for feature in gr_record.features:
                    gr_record.plot_feature(ax, feature, y, linewidth=0.2)
            for match in matches.primer_matches:
                feature = GraphicFeature(start=match.start,
                                         end=match.end,
                                         strand=match.strand,
                                         color="#e85558")
                gr_record.plot_feature(ax, feature, y, linewidth=0.2)

        if plot_coverage:
            ax.fill_between(range(len(self.coverage)),
                            self.coverage,
                            zorder=-2000,
                            alpha=0.2,
                            facecolor="#a3c3f7")
        return ax