4. Divided Bar graphsο
Divided bar graphs are a bar graph in sections, which often total to 100. (100%)
The python file to make divided bar graphs is below.
The required LaTeX files are below.
The custom python modules required are:
A sample text file is below:
4.1. patternsο
For patterns instead of filled colours use:
The python file to make divided bar graphs is below.
The required LaTeX files are below.
4.1.1. Python toolsο
A small python script to generate percetages summing ot 100%:
4.1.2. Example divided bar graphsο
4.1.3. LaTeXο
The .tex file template is shown below.
\documentclass[border=3mm]{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=newest}
% Define custom colors
\definecolor{pastelblue}{rgb}{0.68, 0.78, 0.81}
\definecolor{pastelgreen}{rgb}{0.47, 0.87, 0.47}
\definecolor{pastelyellow}{rgb}{0.99, 0.99, 0.59}
\definecolor{pastelorange}{rgb}{1.0, 0.7, 0.28}
\definecolor{pastelred}{rgb}{1.0, 0.41, 0.38}
\definecolor{pastelpink}{rgb}{1.0, 0.8, 0.86}
\definecolor{pastelteal}{rgb}{0.53, 0.81, 0.92}
\definecolor{pastelmagenta}{rgb}{0.99, 0.6, 0.8}
\definecolor{pastellime}{rgb}{0.8, 1.0, 0.6}
\definecolor{pastelpeach}{rgb}{1.0, 0.9, 0.71}
% Define cycle list macro
\newcommand{\mycyclelist}{
cycle list={
{fill=pastelblue},
{fill=pastelgreen},
{fill=pastelyellow},
{fill=pastelorange},
{fill=pastelred},
{fill=pastelpink},
{fill=pastelteal},
{fill=pastelmagenta},
{fill=pastellime},
{fill=pastelpeach},
}
}
% Define list of x values
\def\myxvalues{{<<numbers_string>>}}
% Define list of legend labels
\def\mylegendlabels{{<<numbers_labels>>}}
\begin{document}
\begin{tikzpicture}
\begin{axis}[
xbar stacked,
bar width=15pt,
axis lines=none,
xmin=0,
xmax=<<xmax>>,
height=2.2cm,
width=10cm,
title={<<plot_title>>},
enlarge y limits={abs=0.1},
nodes near coords,
legend style={at={(0.5,-0.1)}, anchor=north, legend columns=5, yshift=-1mm},
legend image code/.code={
\draw[#1] (0cm,-0.1cm) rectangle (0.6cm,0.1cm);
},
\mycyclelist
]
\pgfplotsinvokeforeach{0,...,<<numbers_loop_max>>}{
\pgfmathparse{\mylegendlabels[#1]}
\edef\tmp{\pgfmathresult}
\pgfmathparse{\myxvalues[#1]}
\edef\tmpx{\pgfmathresult}
\addplot coordinates {(\tmpx,0)};
\addlegendentryexpanded{\tmp};
}
\end{axis}
\end{tikzpicture}
\end{document}
The .tex file patterns template is shown below.
\documentclass[border=3mm]{standalone}
\usepackage{pgfplots}
\usetikzlibrary{patterns}
\pgfplotsset{compat=newest}
% red, green, blue, cyan, magenta, yellow, black, gray, darkgray, lightgray, brown, lime, olive, orange, pink, purple, teal, violet and white
% Define cycle list macro
\newcommand{\mycyclelist}{
cycle list={
{pattern=vertical lines, pattern color=darkgray},
{pattern=dots, pattern color=purple},
{pattern=north east lines, pattern color=olive},
{pattern=crosshatch dots, pattern color=violet},
{pattern=horizontal lines, pattern color=teal},
{pattern=north west lines, pattern color=red},
{pattern=grid, pattern color=blue},
{pattern=fivepointed stars, pattern color=magenta},
{pattern=bricks, pattern color=cyan},
{pattern=sixpointed stars, pattern color=orange},
}
}
% Define list of x values
\def\myxvalues{{<<numbers_string>>}}
% Define list of legend labels
\def\mylegendlabels{{<<numbers_labels>>}}
\begin{document}
\begin{tikzpicture}
\begin{axis}[
xbar stacked,
bar width=15pt,
axis lines=none,
xmin=0,
xmax=<<xmax>>,
height=2.2cm,
width=10cm,
title={<<plot_title>>},
enlarge y limits={abs=0.1},
nodes near coords,
legend style={at={(0.5,-0.1)}, anchor=north, legend columns=5, yshift=-1mm},
legend image code/.code={
\draw[#1] (0cm,-0.1cm) rectangle (0.6cm,0.1cm);
},
\mycyclelist
]
\pgfplotsinvokeforeach{0,...,<<numbers_loop_max>>}{
\pgfmathparse{\mylegendlabels[#1]}
\edef\tmp{\pgfmathresult}
\pgfmathparse{\myxvalues[#1]}
\edef\tmpx{\pgfmathresult}
\addplot coordinates {(\tmpx,0)};
\addlegendentryexpanded{\tmp};
}
\end{axis}
\end{tikzpicture}
\end{document}
4.1.4. Txt fileο
The .txt file is shown below.
3 lines store data:
line 1: the plot title
line 2: a comma separated sequence of numeric values
line 3: a comma separated sequence of labels for the values
A maximum number of entries for line 2 and line 3 is 10.
Some characters need to be escaped manually such as & for & and % for % if used in line 1 or 3.
Favourite zoo animals
53,50,46,40,38,35,29,25,22,14
monkeys,dolphins,penguins,lions,giraffes,elephants,otters,bears,zebras,hippos
4.1.5. Png fileο
The .png file is shown below.
4.1.6. Python codeο
The python code is shown below.
from pathlib import Path
import subprocess
import os
from tkinter import filedialog
import time
import magick_pdf_to_png
currfile_dir = Path(__file__).parent
tex_template_path = currfile_dir / "divided_bar_chart_template.tex"
def convert_to_pdf(tex_path, outputdir):
tex_path = Path(tex_path).resolve()
outputdir = Path(outputdir).resolve()
# for testing
# print(f"tex_path: {tex_path}")
# print(f"outputdir: {outputdir}")
try:
# Generate the PDF
subprocess.run(["latexmk", "-pdf", "-outdir=" + str(outputdir), str(tex_path)], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
# # Clean auxiliary files after successful PDF generation
subprocess.run(["latexmk", "-c", "-outdir=" + str(outputdir), str(tex_path)], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
# for hosted remove stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL for debugging any errors
# Remove the .tex file manually
if tex_path.exists():
os.remove(tex_path)
except subprocess.CalledProcessError as e:
print(f"Error: {e}")
def get_list_from_str(data_string):
if ", " in data_string:
data_list = data_string.split(", ")
elif "," in data_string:
data_list = data_string.split(",")
else:
data_list = data_string.split(" ")
return data_list
def get_list_nums_from_str(num_string_list):
have_dec = any("." in s for s in num_string_list)
if have_dec:
num_list = [float(n) for n in num_string_list]
else:
num_list = [int(n) for n in num_string_list]
return num_list
def get_substrings_from_string(data_string):
new_string = ""
for s in data_string.split(","):
new_string += f'"{s}",'
new_string = new_string[:-1]
return new_string
def get_file_data(filename):
# open the text file and read the numbers
with open(filename) as f:
# read the first line and store it in a variable
plot_title = f.readline().strip()
# read the second line and store it in a variable
numbers_string = f.readline().strip()
# read the third line and store it in a variable
numbers_labels = f.readline().strip()
#
numbers_list = get_list_from_str(numbers_string)
numbers_labels = get_substrings_from_string(numbers_labels)
# process numbers
numbers_loop_max = str(len(numbers_list) - 1)
numbers_list = get_list_nums_from_str(numbers_list)
xmax = str(sum(numbers_list))
return plot_title, numbers_string, numbers_labels, numbers_loop_max, xmax
def main():
data_filename = filedialog.askopenfilename(initialdir=Path(currfile_dir))
if data_filename == "":
print("Exited, by clicking Cancel")
return
plot_title, numbers_string, numbers_labels, numbers_loop_max, xmax = get_file_data(
data_filename
)
# print(plot_title, numbers_string, numbers_labels, numbers_loop_max)
# Create a Path object from the file path
path_obj = Path(data_filename)
# Get the file name from the Path object using the name attribute
filename = path_obj.stem
# filename = input("Enter the base filename to be added to the prefix dp_: \n")
# if not filename:
# filename = "dp_1"
# set names of files that are made
tex_output_path = currfile_dir / f"{filename}.tex"
pdf_path = currfile_dir / f"{filename}.pdf"
png_path = currfile_dir / f"{filename}.png"
# Read in the LaTeX template file
with open(tex_template_path, "r") as infile:
tex_template_txt = infile.read()
# Replace the placeholders in the LaTeX template
tex_template_txt = tex_template_txt.replace("<<plot_title>>", plot_title)
tex_template_txt = tex_template_txt.replace(
"<<numbers_loop_max>>", numbers_loop_max
)
tex_template_txt = tex_template_txt.replace("<<xmax>>", xmax)
tex_template_txt = tex_template_txt.replace("<<numbers_labels>>", numbers_labels)
tex_template_txt = tex_template_txt.replace("<<numbers_string>>", numbers_string)
# Write the question tex to an output file
with open(tex_output_path, "w") as outfile:
outfile.write(tex_template_txt)
# Wait for the files to be created
time.sleep(1)
# Convert the LaTeX files to PDFs
convert_to_pdf(tex_output_path, currfile_dir)
# Wait for the files to be created
time.sleep(1)
# Convert the PDFs to PNGs
magick_pdf_to_png.convert_pdf_to_png(pdf_path, png_path)
if __name__ == "__main__":
print("starting")
main()
print("finished")
The python code for patterns is the same as above except for the template used within it.
from pathlib import Path
import subprocess
import os
from tkinter import filedialog
import time
import magick_pdf_to_png
currfile_dir = Path(__file__).parent
tex_template_path = currfile_dir / "divided_bar_chart_patterns_template.tex"
def convert_to_pdf(tex_path, outputdir):
tex_path = Path(tex_path).resolve()
outputdir = Path(outputdir).resolve()
# for testing
# print(f"tex_path: {tex_path}")
# print(f"outputdir: {outputdir}")
try:
# Generate the PDF
subprocess.run(["latexmk", "-pdf", "-outdir=" + str(outputdir), str(tex_path)], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
# # Clean auxiliary files after successful PDF generation
subprocess.run(["latexmk", "-c", "-outdir=" + str(outputdir), str(tex_path)], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
# for hosted remove stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL for debugging any errors
# Remove the .tex file manually
if tex_path.exists():
os.remove(tex_path)
except subprocess.CalledProcessError as e:
print(f"Error: {e}")
def get_list_from_str(data_string):
if ", " in data_string:
data_list = data_string.split(", ")
elif "," in data_string:
data_list = data_string.split(",")
else:
data_list = data_string.split(" ")
return data_list
def get_list_nums_from_str(num_string_list):
have_dec = any("." in s for s in num_string_list)
if have_dec:
num_list = [float(n) for n in num_string_list]
else:
num_list = [int(n) for n in num_string_list]
return num_list
def get_substrings_from_string(data_string):
new_string = ""
for s in data_string.split(","):
new_string += f'"{s}",'
new_string = new_string[:-1]
return new_string
def get_file_data(filename):
# open the text file and read the numbers
with open(filename) as f:
# read the first line and store it in a variable
plot_title = f.readline().strip()
# read the second line and store it in a variable
numbers_string = f.readline().strip()
# read the third line and store it in a variable
numbers_labels = f.readline().strip()
#
numbers_list = get_list_from_str(numbers_string)
numbers_labels = get_substrings_from_string(numbers_labels)
# process numbers
numbers_loop_max = str(len(numbers_list) - 1)
numbers_list = get_list_nums_from_str(numbers_list)
xmax = str(sum(numbers_list))
return plot_title, numbers_string, numbers_labels, numbers_loop_max, xmax
def main():
data_filename = filedialog.askopenfilename(initialdir=Path(currfile_dir))
if data_filename == "":
print("Exited, by clicking Cancel")
return
plot_title, numbers_string, numbers_labels, numbers_loop_max, xmax = get_file_data(
data_filename
)
# print(plot_title, numbers_string, numbers_labels, numbers_loop_max)
# Create a Path object from the file path
path_obj = Path(data_filename)
# Get the file name from the Path object using the name attribute
filename = path_obj.stem
# filename = input("Enter the base filename to be added to the prefix dp_: \n")
# if not filename:
# filename = "dp_1"
# set names of files that are made
tex_output_path = currfile_dir / f"{filename}.tex"
pdf_path = currfile_dir / f"{filename}.pdf"
png_path = currfile_dir / f"{filename}.png"
# Read in the LaTeX template file
with open(tex_template_path, "r") as infile:
tex_template_txt = infile.read()
# Replace the placeholders in the LaTeX template
tex_template_txt = tex_template_txt.replace("<<plot_title>>", plot_title)
tex_template_txt = tex_template_txt.replace(
"<<numbers_loop_max>>", numbers_loop_max
)
tex_template_txt = tex_template_txt.replace("<<xmax>>", xmax)
tex_template_txt = tex_template_txt.replace("<<numbers_labels>>", numbers_labels)
tex_template_txt = tex_template_txt.replace("<<numbers_string>>", numbers_string)
# Write the question tex to an output file
with open(tex_output_path, "w") as outfile:
outfile.write(tex_template_txt)
# Wait for the files to be created
time.sleep(1)
# Convert the LaTeX files to PDFs
convert_to_pdf(tex_output_path, currfile_dir)
# Wait for the files to be created
time.sleep(1)
# Convert the PDFs to PNGs
magick_pdf_to_png.convert_pdf_to_png(pdf_path, png_path)
if __name__ == "__main__":
print("starting")
main()
print("finished")