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๏ƒ

zoo

participants

crust_elements

icecream

outdoors

travel


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.
../../_images/dbc_zoo.png

4.1.6. Python code๏ƒ

The python code is shown below.
from pathlib import Path
import subprocess
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, currfile_dir, aux_path):
    """
    Converts a TeX file to PDF format using pdfLaTeX.

    Args:
        tex_path (str): The path to the TeX file.
        currfile_dir (str): The path to the directory where the TeX file is located.
        aux_path (str): The path to the directory where auxiliary files will be stored.

    Returns:
        subprocess.CompletedProcess: A subprocess.CompletedProcess object containing information about the completed process.

    Raises:
        FileNotFoundError: If the TeX file does not exist.
        subprocess.CalledProcessError: If pdfLaTeX returns a non-zero exit code.
    """
    result = subprocess.run(
        [
            "pdfLaTeX",
            tex_path,
            "-output-directory",
            currfile_dir,
            "-aux-directory",
            aux_path,
        ],
        stdout=subprocess.PIPE,
    )


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_1st"
    # 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"
    aux_path = currfile_dir / "temp"
    # 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, aux_path)

    # 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
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, currfile_dir, aux_path):
    """
    Converts a TeX file to PDF format using pdfLaTeX.

    Args:
        tex_path (str): The path to the TeX file.
        currfile_dir (str): The path to the directory where the TeX file is located.
        aux_path (str): The path to the directory where auxiliary files will be stored.

    Returns:
        subprocess.CompletedProcess: A subprocess.CompletedProcess object containing information about the completed process.

    Raises:
        FileNotFoundError: If the TeX file does not exist.
        subprocess.CalledProcessError: If pdfLaTeX returns a non-zero exit code.
    """
    result = subprocess.run(
        [
            "pdfLaTeX",
            tex_path,
            "-output-directory",
            currfile_dir,
            "-aux-directory",
            aux_path,
        ],
        stdout=subprocess.PIPE,
    )


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_1st"
    # 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"
    aux_path = currfile_dir / "temp"
    # 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, aux_path)

    # 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")