Results & graphs
Graphs
Barchart
Colormap
Fitplot
Histogram
Linechart
Scatterplot
Scatterplot 4D
Matplotlib
Use python’s matplotlib (MPL) to create any graph from the library. See the examples.

import pandas as pd
import numpy as np
from matplotlib.figure import Figure
# predefined globals:
# df: pd.DataFrame with the input table of input "a"
# mdf: pd.DataFrame with the input column metadata table of input "a"
# fig: Figure to be filled by this node
# axis_secs_to_HM(axis, major_secs=3600, minor_secs=None)
# style_figure(fig, icon: str = "") - set colors to the figure to match NIS colors
# generated data
x = [ 3600*i for i in range(10) ]
y = [ i**2 for i in range(10) ]
# plot the graph
fig = Figure(figsize=(12, 4))
ax = fig.add_subplot(1, 1, 1)
ax.plot(x, y, marker='o', linewidth=2)
ax.set_title("Object Count vs Time")
ax.set_xlabel("Time (hh:mm:ss)")
ax.set_ylabel("Count")
ax.grid(True)
# formats time axis from seconds to HH:MM
axis_secs_to_HM(ax.xaxis)
# set colors to the figure to match NIS light/dark colors
style_figure(fig)The above python code produces following line chart.


To see how to make column access more robust for later editing, see the Create Column
Helper functions and variables
Below are global variables and functions available to the user code.
# used in figure rendering
# may be modified in the script
savefig_kwargs: dict = {
'format': 'png',
'bbox_inches': 'tight',
'pad_inches': 0.25
}
# defualt color cycler
datacolors_light9 = [ ... ]
# color cycler for object IDs
datacolors_objects = [ ... ]
# color cycler for track IDs
datacolors_tracks = [ ... ]
datacolors = datacolors_light9
plt.rcParams['axes.prop_cycle'] = cycler(color=datacolors)
# converts seconds into HH:MM with optional major and minor tick period
def axis_secs_to_HM(axis, major_secs=3600, minor_secs=None):
...
def set_color_cycler_default(ax):
ax.set_prop_cycle(cycler(color=datacolors))
def set_color_cycler_objects(ax):
ax.set_prop_cycle(cycler(color=datacolors_objects))
def set_color_cycler_tracks(ax):
ax.set_prop_cycle(cycler(color=datacolors_tracks))
# styles the figure so that it uses light/dark NIS scheme
# icon is one of "histo", "line" and "scatter"
def style_figure(fig, icon: str = ""):
...Use LLMs with Python nodes
It is possible to ask large language models (LLMs) like ChatGPT, Gemini or Copilot to generate python code
that will render the graph. To simplify the interaction with the LLMs there is a button Copy prompt for a LLM
which prepares a prompt ready to be pasted into the LLMs. The user has to replace the <USER TASK HERE>.
At the end of the prompt:
Task (provided by the user):
ENTER YOUR QUESTION HERE
Note that it is a good practice to create a new chat for the prompt so that it doesn’t share context with unrelated conversation.
Copy and paste the python code – replace it: Ctrl+A, Ctrl+V
- Example of a Scatterplot with confidence interval (movie)
- In the movie we show how to interact with the LLMs.
- Example of a Histogram with a Gaussian fit
- When the following question is added to the generated prompt:
Task (provided by the user):
Plot a histogram (PDF) of circularity in 20 bins.
Overlay it with a fitted gaussian curve with mean and stdev in the legend.
The ChatGPT 4o outputs code that produced following graph:

- Example of a 3D scatterplot
- When the following question is added to the generated prompt:
Task (provided by the user):
Plot Bin shape factor vs. Circularity vs. Area into 3D scatterplot.
The ChatGPT 4o outputs code that produced following graph:

- Example of a Pie chart
- When the following question is added to the generated prompt:
Task (provided by the user):
Create a Pie chart of the Class column.
The ChatGPT 4o outputs code that produced following graph:

Layout
Nodes in layout are used for nice presentation of the results around the image. All nodes in this group are used together. Horizontal and stacked layouts are inputs to the Display node.
Display
Display is the node that defines how the results layout with the image in NIS-Express main window. If not present (or not selected for preview) all results are stacked below the image. When there is more than than one result (e.g. a table and graph) it makes sense to include the display and Stacked layouts.
Depending on how inputs are connected some or all quadrants are used.
It has three inputs:
- Left (mandatory) that is the main one below the image.
- Right (optional) that splits the main bottom pane below the image in two parts left and right.
- Side (optional) that occupies the space besides the image.
- Report (optional) allows for a HTML Report to be shown on a button.
Only Stacked Layout and Horizontal Layout can be connected to the first three inputs.
Parameters
See also: Layout (group), Presentation of results
Horizontal
Horizontal layout is an input to the Display node. It allows for further division of the space in the panes (typically Left and Right).
It can take following nodes as input:
- all nodes from Graph group,
- Stacked layout,
- all nodes from Wellplate group
- all nodes from Tables group
- all nodes from Image group
and the output should be connected either to Display node.
See also: Layout (group)
Stacked
Stack layout is an input to the Display node. It allows for results to be stack one above the other in a pane.
It can take following nodes as input:
- all nodes from Graph group,
- all nodes from Wellplate group
- all nodes from Tables group
- all nodes from Image group
and the output should be connected either to Horizontal layout or to Display node.
See also: Layout (group)
Wellplate
Bars
Barstack
Boxplot
Dosing
Heatmap
Image
Labeling
Linechart
Violin
Tables
Summary
Table
Cross Table
Table with Stats
Image
Object Catalog
Frame
Reports
Report
HTML Report
Build a final static HTML report from connected inputs (A, B, C, …) using a Jinja template with optional Python and CSS customization.
General usage
What each part is for:
- Template (
source): Main report structure (Jinja + HTML). - Python (
python): Optional helper logic executed before rendering. - CSS (
css): Optional styling for the final report. - Inputs (
A,B, …): Available in template as lowercase variables (a,b, …).
Template basics:
{{ a }}or{{ a | print }}renders inputa{{ a | pane(i) }}selects pane indexi(0-based){{ a | pane(i) | item(j) }}selects itemjwithin panei{{ NOTES }}inserts user notes
Starter template:
<h1>Report</h1>
{% if NOTES is defined %}
<h2>Notes</h2>
{{ NOTES }}
{% endif %}
<h2>Main Output</h2>
{% if a is defined %}
{{ a | print }}
{% else %}
<p>Input A is not connected.</p>
{% endif %}Practical tips:
- Guard missing inputs (
if a is defined). - Guard empty lists before using
min/max. - Prefer column IDs over visible titles in
show/hide/filter. - For an exclusive column set, use
hide='.*'with explicitshow=[...]. - Build templates incrementally:
a->a | print-> add filters/loops.
AI-assisted template generation
Recommended AI workflow:
- Click Copy prompt for LLM in the node.
- Paste into your LLM tool.
- Describe the desired report sections and required data.
- Paste generated Jinja back into the template field.
- Run and iterate.