Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add plotly plotting functionality #50

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ python:
install:
- pip install coveralls pytest-cov==2.6 pytest==3.2.3 Biopython bcbio-gff
- pip install bokeh pandas
- pip install plotly
- pip install -e .
# command to run tests
script:
Expand Down
6 changes: 6 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ If you intend to use the bokeh features, you need to also install Bokeh and Pand

(sudo) pip install bokeh pandas

If you intend to use the plotly features, you need to also install Plotly and Pandas:

.. code:: python

(sudo) pip install plotly pandas

To parse GFF files, install the ``bcbio-gff`` library:

.. code::
Expand Down
3 changes: 2 additions & 1 deletion dna_features_viewer/GraphicRecord/GraphicRecord.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@

from .MatplotlibPlottableMixin import MatplotlibPlottableMixin
from .BokehPlottableMixin import BokehPlottableMixin
from .PlotlyPlottableMixin import PlotlyPlottableMixin


class GraphicRecord(MatplotlibPlottableMixin, BokehPlottableMixin):
class GraphicRecord(MatplotlibPlottableMixin, BokehPlottableMixin, PlotlyPlottableMixin):
"""Set of Genetic Features of a same DNA sequence, to be plotted together.

Parameters
Expand Down
182 changes: 182 additions & 0 deletions dna_features_viewer/GraphicRecord/PlotlyPlottableMixin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
try:
import plotly.graph_objects as go

PLOTLY_AVAILABLE = True
except ImportError:
PLOTLY_AVAILABLE = False

try:
import pandas as pd

PANDAS_AVAILABLE = True
except ImportError:
PANDAS_AVAILABLE = False

import matplotlib.pyplot as plt


class PlotlyPlottableMixin:
def plotly_feature_patch(
self,
start,
end,
strand,
figure_width=5,
width=0.4,
level=0,
arrow_width_inches=0.05,
**kwargs
):
"""Return a dict with points coordinates of a plotly shape. Same as bokeh feature arrow

Parameters
----------

start, end, strand

"""
hw = width / 2.0
x1, x2 = (start, end) if (strand >= 0) else (end, start)
bp_per_width = figure_width / self.sequence_length
delta = arrow_width_inches / bp_per_width
if strand >= 0:
head_base = max(x1, x2 - delta)
else:
head_base = min(x1, x2 + delta)
result = dict(
xs=[x1, x1, head_base, x2, head_base, x1],
ys=[e + level for e in [-hw, hw, hw, 0, -hw, -hw]],
)
result.update(kwargs)
return result

def plot_with_plotly(self, figure_width=5, figure_height="auto", tools="auto"):
"""Plot the graphic record using Plotly.
The returned fig object can be used in a Dash dashboard.

Examples
--------

>>>


"""
if not PLOTLY_AVAILABLE:
raise ImportError("``plot_with_plotly`` requires plotly installed.")
if not PANDAS_AVAILABLE:
raise ImportError("``plot_with_plotly`` requires plotly installed.")

# Set up default tools
# if tools == "auto":
# tools = [HoverTool(tooltips="@hover_html"), "xpan,xwheel_zoom,reset,tap"]

# FIRST PLOT WITH MATPLOTLIB AND GATHER INFOS ON THE PLOT
ax, (features_levels, plot_data) = self.plot(figure_width=figure_width)
width, height = [int(100 * e) for e in ax.figure.get_size_inches()]
plt.close(ax.figure)
if figure_height == "auto":
height = int(0.5 * height)
else:
height = 100 * figure_height
height = max(height, 185) # Minimal height to see all icons

max_y = max(
[data["annotation_y"] for f, data in plot_data.items()]
+ list(features_levels.values())
)

fig = go.Figure()

# Update plot width and height
fig.update_layout(
autosize=False,
width=width,
height=height,
margin=dict(l=0, r=20, t=0, b=20)
)

# Update axes properties
fig.update_xaxes(
range=[0, self.sequence_length],
zeroline=False,
)

fig.update_yaxes(
range=[-1, max_y + 1],
zeroline=False,
showline=False,
showgrid=False,
visible=False,
)

# Add patches
for feature, level in features_levels.items():
patch = self.plotly_feature_patch(
feature.start,
feature.end,
feature.strand,
figure_width=figure_width,
level=level,
color=feature.color,
label=feature.label,
hover_html=(
feature.html
if feature.html is not None
else feature.label
),
)
fig.add_trace(
go.Scatter(
x=patch["xs"],
y=patch["ys"],
fill="toself",
mode="lines",
name="",
text=patch["label"],
line_color="#000000",
fillcolor=patch["color"],
)
)

if plot_data != {}:

text_df = pd.DataFrame.from_records(
[
dict(
x=feature.x_center,
y=pdata["annotation_y"],
text=feature.label,
color=feature.color,
)
for feature, pdata in plot_data.items()
]
)

# Scatter trace of text labels
fig.add_trace(go.Scatter(
x=text_df["x"],
y=text_df["y"],
text=text_df["text"],
mode="text",
hoverinfo="skip",
textfont=dict(size=12, family="arial"),
textposition="middle center",
))

# Add segments
for feature, pdata in plot_data.items():
fig.add_shape(
type="line",
x0=feature.x_center,
y0=pdata["annotation_y"],
x1=feature.x_center,
y1=pdata["feature_y"],
line=dict(color="#000000", width=0.5)
)

fig.update_layout(
template="simple_white",
showlegend=False,
)

return fig
Loading