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

matplotlibify #40

Merged
merged 6 commits into from May 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions environment.yml
Expand Up @@ -20,6 +20,8 @@ dependencies:
- matplotlib
- ipympl # interactive matplotlib plots
- seaborn
- plotly
- kaleido
# - altair
# - yellowbrick
# ML, STATS & DEEP LEARNING
Expand Down
Binary file added reports/figures/lattice_a_matplotlibified.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added reports/figures/lattice_a_plotly_default.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions requirements.txt
@@ -1,4 +1,6 @@
importlib_metadata==4.11.4
kaleido
numpy==1.22.4
pillow==9.1.1
plotly
pymatgen==2022.5.19
2 changes: 2 additions & 0 deletions setup.cfg
Expand Up @@ -52,6 +52,8 @@ install_requires =
numpy
pillow
pymatgen
plotly
kaleido


[options.packages.find]
Expand Down
128 changes: 128 additions & 0 deletions src/xtal2png/utils/plotting.py
@@ -0,0 +1,128 @@
from typing import Union

import plotly.graph_objs as go
from plotly import offline


def matplotlibify(
fig: go.Figure,
size: int = 24,
width_inches: Union[float, int] = 3.5,
height_inches: Union[float, int] = 3.5,
dpi: int = 142,
return_scale: bool = False,
) -> go.Figure:
"""Make plotly figures look more like matplotlib for academic publishing.

modified from: https://medium.com/swlh/fa56ddd97539

Parameters
----------
fig : go.Figure
Plotly figure to be matplotlibified
size : int, optional
Font size for layout and axes, by default 24
width_inches : Union[float, int], optional
Width of matplotlib figure in inches, by default 3.5
height_inches : Union[float, int], optional
Height of matplotlib figure in Inches, by default 3.5
dpi : int, optional
Dots per inch (resolution) of matplotlib figure, by default 142. Leave as
default unless you're willing to verify nothing strange happens with the output.
return_scale : bool, optional
If true, then return `scale` which is a quantity that helps with creating a
high-resolution image at the specified absolute width and height in inches.
More specifically:
>>> width_default_px = fig.layout.width
>>> targ_dpi = 300
>>> scale = width_inches / (width_default_px / dpi) * (targ_dpi / dpi)
Feel free to ignore this parameter.

Returns
-------
fig : go.Figure
The matplotlibified plotly figure.

Examples
--------
>>> import plotly.express as px
>>> df = px.data.tips()
>>> fig = px.histogram(df, x="day")
>>> fig.show()
>>> fig = matplotlibify(fig, size=24, width_inches=3.5, height_inches=3.5, dpi=142)
>>> fig.show()

Note the difference between
https://user-images.githubusercontent.com/45469701/171044741-35591a2c-dede-4df1-ae47-597bbfdb89cf.png # noqa: E501
and
https://user-images.githubusercontent.com/45469701/171044746-84a0deb0-1e15-40bf-a459-a5a7d3425b20.png, # noqa: E501
which are both static exports of interactive plotly figures.
"""
font_dict = dict(family="Arial", size=size, color="black")

# app = QApplication(sys.argv)
# screen = app.screens()[0]
# dpi = screen.physicalDotsPerInch()
# app.quit()

fig.update_layout(
font=font_dict,
plot_bgcolor="white",
width=width_inches * dpi,
height=height_inches * dpi,
margin=dict(r=40, t=20, b=10),
)

fig.update_yaxes(
showline=True, # add line at x=0
linecolor="black", # line color
linewidth=2.4, # line size
ticks="inside", # ticks outside axis
tickfont=font_dict, # tick label font
mirror="allticks", # add ticks to top/right axes
tickwidth=2.4, # tick width
tickcolor="black", # tick color
)

fig.update_xaxes(
showline=True,
showticklabels=True,
linecolor="black",
linewidth=2.4,
ticks="inside",
tickfont=font_dict,
mirror="allticks",
tickwidth=2.4,
tickcolor="black",
)
fig.update(layout_coloraxis_showscale=False)

width_default_px = fig.layout.width
targ_dpi = 300
scale = width_inches / (width_default_px / dpi) * (targ_dpi / dpi)

if return_scale:
return fig, scale
else:
return fig


def plot_and_save(fig_path, fig, mpl_kwargs={}, show=False, update_legend=False):
if show:
offline.plot(fig)
fig.write_html(fig_path + ".html")
fig.to_json(fig_path + ".json")
if update_legend:
fig.update_layout(
legend=dict(
font=dict(size=16),
yanchor="bottom",
y=0.99,
xanchor="right",
x=0.99,
bgcolor="rgba(0,0,0,0)",
# orientation="h",
)
)
fig = matplotlibify(fig, **mpl_kwargs)
fig.write_image(fig_path + ".png")
11 changes: 11 additions & 0 deletions tests/xtal2png_test.py
Expand Up @@ -2,6 +2,7 @@
# (test), and back to crystal Structure (test)


import plotly.express as px
from numpy.testing import assert_allclose

from xtal2png.core import XtalConverter
Expand All @@ -12,6 +13,7 @@
rgb_scaler,
rgb_unscaler,
)
from xtal2png.utils.plotting import plot_and_save

rgb_tol = 1 / 255 # should this be 256?
rgb_loose_tol = 1.5 / 255
Expand Down Expand Up @@ -138,6 +140,15 @@ def test_rgb_scaler_unscaler():
assert_allclose(check_output, scaled)


def test_plot_and_save():
df = px.data.tips()
fig = px.histogram(df, x="day")
plot_and_save("reports/figures/tmp", fig, mpl_kwargs={})


# TODO: test_matplotlibify with assertion


if __name__ == "__main__":
test_element_wise_scaler_unscaler()
test_rgb_scaler_unscaler()
Expand Down