-
Notifications
You must be signed in to change notification settings - Fork 12
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
Extract page layout and use it in plotting #58
base: font-and-figure-info
Are you sure you want to change the base?
Changes from all commits
a88ae62
9b3db66
bc29b0f
1eeb819
94b6cd6
f150355
03ff364
0627b5f
1699e57
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,10 +22,3 @@ the following features: | |
- The [ACMart fonts](https://github.com/opencompl/paper-template/blob/master/acmart.cls#L720-L728) | ||
use [Inconsolata](https://ctan.org/pkg/inconsolata?lang=en) for monospace, | ||
[Libertine](https://ctan.org/pkg/libertine?lang=en) for serif, and [newtx](https://ctan.org/pkg/newtx?lang=en) for math. | ||
- The Libertine `ttf` can be obtained as "linux libertine" on distros, and Inconsolata `ttf` as "Inconsolata". | ||
- The default font size of the body is `11pt`. | ||
- Figures | ||
- The paper is of A4 size, which in *physical units* is `210 x 297 mm`. | ||
- Choose figure size in *physical units* (`mm`/`inch`) based on how much of A4 it occupies and export to PDF. | ||
- Import figure PDF into paper using `\includegraphics{path/to/figure}`. This occupies the desired space in the paper; there is need to use | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this has been integrated to the plotting script |
||
`\includegraphics[width=<insert-width-here>]{path/to/figure}`. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,6 +55,7 @@ | |
\usepackage{thmtools} % required for autoref to lemmas | ||
\usepackage{algorithm} | ||
\usepackage[noend]{algpseudocode} | ||
\usepackage{xfp} | ||
|
||
\input{tex/setup.tex} | ||
\input{tex/acm.tex} | ||
|
@@ -129,6 +130,44 @@ | |
|
||
\newdelimitedcommand{toolname}{Tool} | ||
|
||
% helper macro to fully expand dimension expressions | ||
\newcommand{\expanddim}[1]{\the\dimexpr#1\relax} | ||
|
||
% Define the conversion function from any length to inches | ||
\makeatletter | ||
\newcommand{\convertToInches}[2]{% | ||
\edef\@temp{\expandafter\expandafter\expandafter\strip@pt\expandafter\dimexpr#1\relax}% | ||
\edef#2{\fpeval{round(\@temp / 72.27, 2)}}% | ||
} | ||
\makeatother | ||
|
||
% create a new file for output | ||
\newwrite\myfile | ||
\immediate\openout\myfile=\jobname-layout.csv | ||
|
||
% write widths to file | ||
\makeatletter | ||
\newcommand{\writePageWidths}{ | ||
\newcommand{\mylen}{}% | ||
\convertToInches{\columnwidth}{\mylen}% | ||
\immediate\write\myfile{columnwidth, \mylen}% | ||
\convertToInches{\textwidth}{\mylen}% | ||
\immediate\write\myfile{textwidth, \mylen}% | ||
\convertToInches{\columnsep}{\mylen}% | ||
\immediate\write\myfile{columnsep, \mylen}% | ||
\convertToInches{\textheight}{\mylen}% | ||
\immediate\write\myfile{textheight, \mylen}% | ||
\convertToInches{\paperwidth}{\mylen}% | ||
\immediate\write\myfile{paperwidth, \mylen}% | ||
\convertToInches{\paperheight}{\mylen}% | ||
\immediate\write\myfile{paperheight, \mylen}% | ||
} | ||
\makeatother | ||
|
||
\AtEndDocument{ | ||
\writePageWidths | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could move this to |
||
\usepackage{booktabs} | ||
\newcommand{\ra}[1]{\renewcommand{\arraystretch}{#1}} | ||
|
||
|
@@ -520,8 +559,12 @@ \subsubsection{Plots} We use matplotlib to create performance | |
setting fonttype to 42. | ||
\end{itemize} | ||
|
||
We recommend considering the relevant layout lengths (e.g., the column width) when choosing figure sizes for plots and other figures, to avoid unnecessary scaling when imported with \texttt{\textbackslash{}includegraphics}. | ||
We export layout lengths (in inches) to \texttt{paper-layout.csv}. | ||
The \texttt{plot.py} script shows how to use this information for \autoref{fig:speedup}. | ||
|
||
\begin{figure} | ||
\includegraphics[width=\columnwidth]{plots/speedup} | ||
\includegraphics{plots/speedup} | ||
\caption{Improved running speed after 4 weeks of training. | ||
} | ||
\label{fig:speedup} | ||
|
@@ -663,6 +706,7 @@ \subsubsection{Managing acronyms automatically} | |
\item To obtain plural form, we use \texttt{\textbackslash{}acp\{ir\}} giving: \acp{ir}. | ||
\end{itemize} | ||
|
||
|
||
\end{draftonly} | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,9 +6,11 @@ | |
import matplotlib.pyplot as plt | ||
import numpy as np | ||
import math | ||
import csv | ||
|
||
from typing import Callable | ||
|
||
|
||
def setGlobalDefaults(): | ||
## Use TrueType fonts instead of Type 3 fonts | ||
# | ||
|
@@ -26,7 +28,7 @@ def setGlobalDefaults(): | |
|
||
## Legend defaults | ||
matplotlib.rcParams['legend.frameon'] = False | ||
|
||
# Hide the right and top spines | ||
# | ||
# This reduces the number of lines in the plot. Lines typically catch | ||
|
@@ -36,7 +38,6 @@ def setGlobalDefaults(): | |
matplotlib.rcParams['axes.spines.right'] = False | ||
matplotlib.rcParams['axes.spines.top'] = False | ||
|
||
matplotlib.rcParams['figure.figsize'] = 5, 2 | ||
|
||
# Color palette | ||
light_gray = "#cacaca" | ||
|
@@ -50,6 +51,7 @@ def setGlobalDefaults(): | |
black = "#000000" | ||
white = "#ffffff" | ||
|
||
|
||
def save(figure, name): | ||
# Do not emit a creation date, creator name, or producer. This will make the | ||
# content of the pdfs we generate more deterministic. | ||
|
@@ -59,9 +61,10 @@ def save(figure, name): | |
|
||
# Close figure to avoid warning about too many open figures. | ||
plt.close(figure) | ||
|
||
print(f'written to {name}') | ||
|
||
|
||
# helper for str_from_float. | ||
# format float in scientific with at most *digits* digits. | ||
# | ||
|
@@ -71,53 +74,63 @@ def save(figure, name): | |
def get_scientific(x: float, digits: int): | ||
# get scientific without leading zeros or + in exp | ||
def get(x: float, prec: int) -> str: | ||
result = f'{x:.{prec}e}' | ||
result = result.replace('e+', 'e') | ||
while 'e0' in result: | ||
result = result.replace('e0', 'e') | ||
while 'e-0' in result: | ||
result = result.replace('e-0', 'e-') | ||
return result | ||
result = f'{x:.{prec}e}' | ||
result = result.replace('e+', 'e') | ||
while 'e0' in result: | ||
result = result.replace('e0', 'e') | ||
while 'e-0' in result: | ||
result = result.replace('e-0', 'e-') | ||
return result | ||
|
||
result = get(x, digits) | ||
len_after_e = len(result.split('e')[1]) | ||
prec = max(0, digits - len_after_e - 2) | ||
return get(x, prec) | ||
|
||
|
||
# format float with at most *digits* digits. | ||
# if the number is too small or too big, | ||
# it will be formatted in scientific notation, | ||
# optionally a suffix can be passed for the unit. | ||
# | ||
# note: this displays different numbers with different | ||
# precision depending on their length, as much as can fit. | ||
def str_from_float(x: float, digits: int = 3, suffix: str = '') -> str: | ||
result = f'{x:.{digits}f}' | ||
before_decimal = result.split('.')[0] | ||
if len(before_decimal) == digits: | ||
return before_decimal | ||
if len(before_decimal) > digits: | ||
# we can't even fit the integral part | ||
return get_scientific(x, digits) | ||
|
||
result = result[:digits + 1] # plus 1 for the decimal point | ||
if float(result) == 0: | ||
# we can't even get one significant figure | ||
return get_scientific(x, digits) | ||
|
||
return result[:digits + 1] | ||
def str_from_float(x: float, digits: int = 2, suffix: str = '') -> str: | ||
result = f'{x:.{digits}f}' | ||
before_decimal = result.split('.')[0] | ||
if len(before_decimal) == digits: | ||
return before_decimal | ||
if len(before_decimal) > digits: | ||
# we can't even fit the integral part | ||
return get_scientific(x, digits) | ||
|
||
result = result[: digits + 1] # plus 1 for the decimal point | ||
if float(result) == 0: | ||
# we can't even get one significant figure | ||
return get_scientific(x, digits) | ||
|
||
return result[: digits + 1] | ||
|
||
|
||
# Attach a text label above each bar in *rects*, displaying its height | ||
def autolabel(ax, rects, label_from_height: Callable[[float], str] =str_from_float, xoffset=0, yoffset=1, **kwargs): | ||
def autolabel( | ||
ax, | ||
rects, | ||
label_from_height: Callable[[float], str] = str_from_float, | ||
xoffset=0, | ||
yoffset=1, | ||
**kwargs, | ||
): | ||
# kwargs is directly passed to ax.annotate and overrides defaults below | ||
assert 'xytext' not in kwargs, "use xoffset and yoffset instead of xytext" | ||
default_kwargs = dict( | ||
xytext=(xoffset, yoffset), | ||
fontsize="smaller", | ||
fontsize="7", | ||
rotation=0, | ||
ha='center', | ||
va='bottom', | ||
textcoords='offset points') | ||
textcoords='offset points', | ||
) | ||
|
||
for rect in rects: | ||
height = rect.get_height() | ||
|
@@ -127,35 +140,38 @@ def autolabel(ax, rects, label_from_height: Callable[[float], str] =str_from_flo | |
**(default_kwargs | kwargs), | ||
) | ||
|
||
|
||
# utility to print times as 1h4m, 1d15h, 143.2ms, 10.3s etc. | ||
def str_from_ms(ms): | ||
def maybe_val_with_unit(val, unit): | ||
return f'{val}{unit}' if val != 0 else '' | ||
def maybe_val_with_unit(val, unit): | ||
return f'{val}{unit}' if val != 0 else '' | ||
|
||
if ms < 1000: | ||
return f'{ms:.3g}ms' | ||
|
||
if ms < 1000: | ||
return f'{ms:.3g}ms' | ||
s = ms / 1000 | ||
ms = 0 | ||
if s < 60: | ||
return f'{s:.3g}s' | ||
|
||
s = ms / 1000 | ||
ms = 0 | ||
if s < 60: | ||
return f'{s:.3g}s' | ||
m = int(s // 60) | ||
s -= 60 * m | ||
if m < 60: | ||
return f'{m}m{maybe_val_with_unit(math.floor(s), "s")}' | ||
|
||
m = int(s // 60) | ||
s -= 60*m | ||
if m < 60: | ||
return f'{m}m{maybe_val_with_unit(math.floor(s), "s")}' | ||
h = int(m // 60) | ||
m -= 60 * h | ||
if h < 24: | ||
return f'{h}h{maybe_val_with_unit(m, "m")}' | ||
|
||
h = int(m // 60) | ||
m -= 60*h; | ||
if h < 24: | ||
return f'{h}h{maybe_val_with_unit(m, "m")}' | ||
d = int(h // 24) | ||
h -= 24 * d | ||
return f'{d}d{maybe_val_with_unit(h, "h")}' | ||
|
||
d = int(h // 24) | ||
h -= 24*d | ||
return f'{d}d{maybe_val_with_unit(h, "h")}' | ||
|
||
def autolabel_ms(ax, rects, **kwargs): | ||
autolabel(ax, rects, label_from_height=str_from_ms, **kwargs) | ||
autolabel(ax, rects, label_from_height=str_from_ms, **kwargs) | ||
|
||
|
||
# Plot an example speedup plot | ||
def plot_speedup(): | ||
|
@@ -164,57 +180,63 @@ def plot_speedup(): | |
women_means = [1.8, 1.5, 1.1, 1.3, 0.9] | ||
|
||
x = np.arange(len(labels)) # the label locations | ||
width = 0.35 # the width of the bars | ||
width = 0.4 # the width of the bars | ||
|
||
fig, ax = plt.subplots() | ||
rects1 = ax.bar(x - width / 2, | ||
men_means, | ||
width, | ||
label='Men', | ||
color=light_blue) | ||
rects2 = ax.bar(x + width / 2, | ||
women_means, | ||
width, | ||
label='Women', | ||
color=dark_blue) | ||
rects1 = ax.bar(x - width / 2, men_means, width, label='Men', color=light_blue) | ||
rects2 = ax.bar(x + width / 2, women_means, width, label='Women', color=dark_blue) | ||
|
||
# Y-Axis Label | ||
# | ||
# Use a horizontal label for improved readability. | ||
ax.set_ylabel('Speedup', | ||
rotation='horizontal', | ||
position=(1, 1.05), | ||
horizontalalignment='left', | ||
verticalalignment='bottom') | ||
ax.set_ylabel( | ||
'Speedup', | ||
rotation='horizontal', | ||
position=(1, 1.05), | ||
horizontalalignment='left', | ||
verticalalignment='bottom', | ||
) | ||
|
||
# Add some text for labels, title and custom x-axis tick labels, etc. | ||
ax.set_xticks(x) | ||
ax.set_xticklabels(labels) | ||
|
||
ax.legend(ncol=100, | ||
loc='lower right', | ||
bbox_to_anchor=(0, 1, 1, 0)) | ||
ax.legend(ncol=100, loc='lower right', bbox_to_anchor=(0, 1, 1, 0)) | ||
|
||
autolabel(ax, rects1) | ||
autolabel(ax, rects2) | ||
|
||
save(fig, 'speedup.pdf') | ||
|
||
|
||
def csv_to_dict(file): | ||
data = {} | ||
|
||
if file is not None: | ||
data = dict(csv.reader(file)) | ||
|
||
return data | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser( | ||
prog='plot', | ||
description='Plot the figures for this paper', | ||
) | ||
parser.add_argument('names', nargs='+', choices=['all', 'speedup']) | ||
parser.add_argument('--layout', type=argparse.FileType('r')) | ||
args = parser.parse_args() | ||
|
||
setGlobalDefaults() | ||
layout = csv_to_dict(args.layout) | ||
|
||
plotAll = 'all' in args.names | ||
|
||
if 'speedup' in args.names or plotAll: | ||
plot_speedup() | ||
with matplotlib.rc_context( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is where the plotting script configuration is applied from the exported CSV values. |
||
{'figure.figsize': (layout.get('columnwidth', 3), 2)} | ||
): | ||
plot_speedup() | ||
|
||
|
||
if __name__ == "__main__": | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unfortunately, this information depends on the paper class used for ACM template papers.