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"), ]
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