Skip to content

Plot Curves

Probably the most important thing to do when analysing mechanical test data is to plot stress-strain curves for the batch so you can compare.

pymechtest uses altair which makes it super easy to plot really nice looking stress-strain curves by default.

The pymechtest plot style is very opinionated, the plots will come out looking the same every time and the API exposes very little in the way of customisation. If you want full control over how you want the plots to look, you might want to just do it manually by loading the data in with .load_all() and then using whatever plotting tool you like.

Every static test method currently supported in pymechtest has a .plot_curves() method. Lets take a look at it...

plot_curves

Creates a nice looking stress strain plot of all the specimens using altair.

Will use the class name to fill in axis labels if not passed, e.g 'Tensile' Strain

Parameters:

Name Type Description Default
title Optional[str]

Title for the plot. Defaults to "{class_name} Stress-Strain Curves".

None
save_path Union[str, pathlib.Path]

str or Pathlike path to save a png of the plot. Requires chrome and selenium. If not passed, plot is simply returned and not saved.

None
save_method Optional[str]

One of 2 altair save methods: 'selenium' or 'node'. if 'selenium' requires a configured geckodriver or chromedriver on PATH. if 'node' requires nodejs installation. Defaults to 'selenium'

'selenium'
x_label Optional[str]

Label for x-axis. Defaults to "{class name}Strain (%)".

None
y_label Optional[str]

Label for y-axis. Defaults to "{class name}Stress (MPa)".

None
height int

Height of the plot. Defaults to 500.

500
width int

Width of the plot. Defaults to 750.

750

Returns:

Type Description
Chart

alt.Chart: Stress strain plot.

Source code in pymechtest/base.py
def plot_curves(
    self,
    title: Optional[str] = None,
    save_path: Optional[Union[str, Path]] = None,
    save_method: Optional[str] = "selenium",
    x_label: Optional[str] = None,
    y_label: Optional[str] = None,
    height: int = 500,
    width: int = 750,
) -> alt.Chart:
    """
    Creates a nice looking stress strain plot of all the specimens using altair.

    Will use the class name to fill in axis labels if not passed, e.g
    'Tensile' Strain

    Args:
        title (str, optional): Title for the plot.
            Defaults to "{class_name} Stress-Strain Curves".

        save_path (Union[str, Path], optional): str or Pathlike path to save
            a png of the plot. Requires chrome and selenium. If not passed, plot
            is simply returned and not saved.

        save_method (str, optional): One of 2 altair save methods:
            'selenium' or 'node'.
            if 'selenium' requires a configured geckodriver or chromedriver on PATH.
            if 'node' requires nodejs installation. Defaults to 'selenium'

        x_label (str, optional): Label for x-axis.
            Defaults to "{class name}Strain (%)".

        y_label (str, optional): Label for y-axis.
            Defaults to "{class name}Stress (MPa)".

        height (int, optional): Height of the plot.
            Defaults to 500.

        width (int, optional): Width of the plot.
            Defaults to 750.

    Returns:
        alt.Chart: Stress strain plot.
    """

    # Altair will warn if over 5,000 rows in a notebook. This is cleanest solution.
    alt.data_transformers.enable("data_server")

    if not x_label:
        x_label = f"{self.__class__.__qualname__} Strain (%)"

    if not y_label:
        y_label = f"{self.__class__.__qualname__} Stress (MPa)"

    if not title:
        title = f"{self.__class__.__qualname__} Stress Strain Curves"

    df = self.load_all()

    chart = (
        alt.Chart(data=df)
        .mark_line(size=1)
        .encode(
            x=alt.X(f"{self.strain_col}:Q", title=x_label),
            y=alt.Y(f"{self.stress_col}:Q", title=y_label),
            color=alt.Color("Specimen ID:N", title="Specimen ID"),
        )
        .properties(title=title, height=height, width=width)
    )

    if save_method not in set(["selenium", "node"]):
        raise ValueError(
            f"Save method must be one of 'selenium' or 'node'. Got: {save_method}"
        )

    if save_path:
        fp = Path(save_path).resolve()
        save(
            chart,
            fp=str(fp),
            scale_factor=6.0,
            method=save_method,
        )

    return chart

Making your plot

In 90% of the cases, you will probably just need to call plot_curves() with no arguments. Again, following the principle of sensible defaults, pymechtest will fill out a lot of the information for you.

For instance, if you're calling plot_curves() from an instance of the Tensile class, the title will be "Tensile Stress Strain Curves" and the axes titles will be "Tensile Stress" and "Tensile Strain".

Like so...

plot_curves

Adjusting your plot

If you do want to specify things like the size of the plot, the title, the axis labels etc. pymechtest allows you to do that!

Set the Title

If you don't want the default title of {class_name} Stress Strain Curves you can set your own using the title argument:

from pymechtest import Tensile

tens = Tensile(folder = "path/to/raw/data", id_row = 3, header = 8)

tens.plot_curves(title = "My Special Title")

plot_title

Set the Axis Labels

Similarly, you can set the axis labels if you like:

from pymechtest import Tensile

tens = Tensile(folder = "path/to/raw/data", id_row = 3, header = 8)

tens.plot_curves(title = "My Special Title", x_label = "Super Secret x Axis", y_label = "Super Secret y Axis")

plot_title_labels

Saving your plot

Now making all these nice graphs wouldn't be much good if you couldn't save them to use later!

Luckily, you can do that too! By default, pymechtest won't save anything it will just show you the plot (if you're in a jupyter notebook). If you want it to save your plot somewhere, you need to pass it a path:

from pymechtest import Tensile

tens = Tensile(folder = "path/to/raw/data", id_row = 3, header = 8)

tens.plot_curves(save_path = "path/to/graph.png")

You must pass a valid file extension for this bit, which is one of:

  • .png
  • .svg

Then pymechtest will then save your graph to the path you specify.

Note about saving

I've mentioned before that pymechtest uses altair to generate the graphs. The way altair works is it turns your data into a vega or vega-lite schema (similar to JSON), this is then interpreted by the inner workings of altair and then rendered in your jupyter notebook (or supporting IDE) by javascript running in the browser (or the IDE).

To turn the graph into a static image (.png or .svg) altair converts the javascript rendering into an image file.

In order to do this, it needs to pipe the schema through a headless browser (or use nodejs). Which means you need to have installed one of the following options:

1) Google Chrome and the latest version of chromedriver

2) Firefox and the latest version of geckodriver

3) nodejs

The first two options are super easy to install and once you've done it once that's it you don't need to worry about it again! Let's face it, you probably already have either firefox or chrome anyway!

Installing nodejs is probably easy too, I've personally only ever done this with homebrew on a mac or with conda inside a conda environment:

# with homebrew
brew install node

# with conda
conda install -c conda-forge nodejs

pymechtest's default is to use the selenium and browser option, so if you save your graph as in the example above (without passing anything to the save_method argument) this is the method that will be used.

If you have chromedriver and chrome it will use that, and if you have geckodriver and firefox it will use that.

If you want to use nodejs, you need to pass node to the save_method argument, like so:

tens.plot_curves(save_path = "path/to/graph.png", save_method = "node")

More info on this can be found in altair saver.