Ecosystem Integration
GnuplotEx integrates seamlessly with the Elixir machine learning and data science ecosystem, providing direct plotting support for Nx tensors, Explorer DataFrames, and specialized ML visualization helpers.
Installation
All ecosystem integrations are optional. Add the dependencies you need to your mix.exs:
def deps do
[
{:gnuplot_ex, "~> 0.1"},
# Optional: For Nx tensor support
{:nx, "~> 0.7"},
# Optional: For DataFrame support (uses Polars backend)
{:explorer, "~> 0.8"}
]
endNx Tensor Support
GnuplotEx can directly plot Nx tensors with automatic dimension handling.
1D Tensors (Line Plots)
1D tensors are treated as y-values with auto-generated x indices:
tensor = Nx.tensor([1.0, 4.0, 2.0, 8.0, 5.0, 7.0, 3.0])
GnuplotEx.new()
|> GnuplotEx.line(tensor, label: "Signal")
|> GnuplotEx.title("1D Tensor as Line Plot")
|> GnuplotEx.render(:svg)2D Tensors (Scatter/Line Plots)
2D tensors with 2 columns are treated as [x, y] points:
# Generate some 2D points
tensor = Nx.tensor([
[1.0, 2.0],
[2.0, 4.0],
[3.0, 6.0],
[4.0, 8.0],
[5.0, 10.0]
])
GnuplotEx.new()
|> GnuplotEx.scatter(tensor, label: "Linear Relationship")
|> GnuplotEx.title("2D Tensor Scatter Plot")
|> GnuplotEx.render(:svg)2D Tensors as Heatmaps
2D tensors with more than 3 columns are treated as grid data for heatmaps:
# Create a 2D matrix for heatmap
matrix = Nx.tensor([
[1, 2, 3, 4, 5],
[2, 4, 6, 8, 10],
[3, 6, 9, 12, 15],
[4, 8, 12, 16, 20],
[5, 10, 15, 20, 25]
])
# Convert to list for surface plots
GnuplotEx.new()
|> GnuplotEx.surface(Nx.to_list(matrix))
|> GnuplotEx.palette(:viridis)
|> GnuplotEx.title("2D Matrix Heatmap")
|> GnuplotEx.render(:svg)3D Tensors (Scatter Plots)
2D tensors with 3 columns are treated as [x, y, z] points:
# 3D point cloud
tensor = Nx.tensor([
[1.0, 2.0, 3.0],
[2.0, 3.0, 4.0],
[3.0, 4.0, 5.0],
[4.0, 5.0, 6.0]
])
GnuplotEx.new()
|> GnuplotEx.scatter3d(tensor, label: "3D Points")
|> GnuplotEx.title("3D Tensor Scatter")
|> GnuplotEx.render(:svg)Explorer DataFrame Support
Explorer DataFrames can be plotted directly with automatic column type detection.
Basic Usage
alias Explorer.DataFrame
df = DataFrame.new(%{
x: [1, 2, 3, 4, 5],
y: [2, 4, 6, 8, 10]
})
GnuplotEx.new()
|> GnuplotEx.scatter(df, label: "DataFrame Points")
|> GnuplotEx.title("DataFrame Scatter Plot")
|> GnuplotEx.render(:svg)Column Selection
Explicitly specify which columns to use:
df = DataFrame.new(%{
time: [1, 2, 3, 4, 5],
temperature: [20, 22, 21, 23, 22],
humidity: [50, 52, 48, 55, 53]
})
# Plot temperature over time
GnuplotEx.new()
|> GnuplotEx.line(df, x: :time, y: :temperature, label: "Temperature")
|> GnuplotEx.title("Temperature Over Time")
|> GnuplotEx.render(:svg)
# Or use explicit columns list
GnuplotEx.new()
|> GnuplotEx.line(df, columns: [:time, :humidity], label: "Humidity")
|> GnuplotEx.render(:svg)Working with Real Data
# Load CSV data
df = Explorer.DataFrame.from_csv!("data.csv")
# Auto-detect columns (uses first two numeric columns)
GnuplotEx.new()
|> GnuplotEx.scatter(df)
|> GnuplotEx.title("Data Analysis")
|> GnuplotEx.render(:svg)ML Visualization Helpers
GnuplotEx includes specialized helpers for common machine learning visualization tasks.
Loss Curves
Plot training and validation loss over epochs:
alias GnuplotEx.ML.Loss
train_loss = [0.9, 0.7, 0.5, 0.35, 0.25, 0.18, 0.12, 0.08]
val_loss = [0.95, 0.75, 0.55, 0.42, 0.35, 0.30, 0.28, 0.27]
Loss.plot(train_loss, val_loss,
title: "Model Training Progress",
x_label: "Epoch",
y_label: "Cross-Entropy Loss"
)
|> GnuplotEx.render(:svg)Plot multiple metrics:
metrics = %{
train_loss: [0.9, 0.7, 0.5, 0.3, 0.2],
val_loss: [0.95, 0.75, 0.55, 0.4, 0.35],
train_acc: [0.6, 0.7, 0.8, 0.88, 0.92],
val_acc: [0.55, 0.68, 0.78, 0.82, 0.85]
}
Loss.plot_metrics(metrics,
title: "Training Metrics",
x_label: "Epoch"
)
|> GnuplotEx.render(:svg)Confusion Matrix
Visualize classification results:
alias GnuplotEx.ML.Confusion
# Binary classification
matrix = [
[85, 15], # True Negative, False Positive
[10, 90] # False Negative, True Positive
]
classes = ["Negative", "Positive"]
Confusion.plot(matrix, classes,
title: "Binary Classification Results"
)
|> GnuplotEx.render(:svg)Multi-class with normalization:
matrix = [
[50, 3, 2],
[4, 45, 1],
[2, 2, 46]
]
classes = ["Cat", "Dog", "Bird"]
Confusion.plot(matrix, classes,
normalize: true,
title: "Normalized Confusion Matrix"
)
|> GnuplotEx.render(:svg)Calculate metrics from confusion matrix:
accuracy = Confusion.accuracy(matrix) # Overall accuracy
precision = Confusion.precision(matrix) # Per-class precision
recall = Confusion.recall(matrix) # Per-class recall
f1 = Confusion.f1_score(matrix) # Per-class F1 scoreROC Curves
Plot Receiver Operating Characteristic curves:
alias GnuplotEx.ML.ROC
# From precomputed FPR/TPR
fpr = [0.0, 0.1, 0.2, 0.4, 0.6, 1.0]
tpr = [0.0, 0.5, 0.7, 0.85, 0.95, 1.0]
ROC.plot(fpr, tpr,
auc: 0.87,
title: "ROC Curve"
)
|> GnuplotEx.render(:svg)Calculate ROC curve from scores and labels:
scores = [0.9, 0.8, 0.7, 0.6, 0.4, 0.3, 0.2, 0.1]
labels = [1, 1, 0, 1, 0, 1, 0, 0]
{fpr, tpr, _thresholds} = ROC.calculate_curve(scores, labels)
auc = ROC.calculate_auc(fpr, tpr)
ROC.plot(fpr, tpr, auc: auc)
|> GnuplotEx.render(:svg)Multi-class ROC curves:
roc_data = %{
"Cat" => {fpr_cat, tpr_cat, 0.92},
"Dog" => {fpr_dog, tpr_dog, 0.88},
"Bird" => {fpr_bird, tpr_bird, 0.95}
}
ROC.plot_multiclass(roc_data,
title: "One-vs-Rest ROC Curves"
)
|> GnuplotEx.render(:svg)Embedding Visualization
Visualize dimensionality reduction results (t-SNE, UMAP, PCA):
alias GnuplotEx.ML.Embeddings
# 2D embeddings with labels
embeddings = [
[1.2, 3.4], [1.5, 3.1], [1.3, 3.8], # Class 0
[5.2, 6.1], [5.5, 6.3], [5.1, 5.9], # Class 1
[3.0, 1.2], [3.2, 1.5], [2.8, 1.1] # Class 2
]
labels = [0, 0, 0, 1, 1, 1, 2, 2, 2]
Embeddings.plot(embeddings, labels,
label_names: ["Cluster A", "Cluster B", "Cluster C"],
title: "t-SNE Visualization"
)
|> GnuplotEx.render(:svg)3D embeddings:
embeddings_3d = [
[1, 2, 3], [1.5, 2.5, 3.5],
[5, 6, 7], [5.5, 6.5, 7.5]
]
labels = [0, 0, 1, 1]
Embeddings.plot(embeddings_3d, labels,
title: "3D t-SNE"
)
|> GnuplotEx.render(:svg)Complete ML Training Example
Here's a complete example showing how to visualize a training loop:
defmodule TrainingVisualizer do
alias GnuplotEx.ML.{Loss, Confusion, ROC}
def visualize_training(history, predictions, labels) do
# 1. Plot training curves
loss_plot = Loss.plot(
history.train_loss,
history.val_loss,
title: "Training Progress"
)
GnuplotEx.save(loss_plot, "training_loss.svg")
# 2. Plot all metrics
metrics_plot = Loss.plot_metrics(%{
train_loss: history.train_loss,
val_loss: history.val_loss,
train_acc: history.train_acc,
val_acc: history.val_acc
})
GnuplotEx.save(metrics_plot, "training_metrics.svg")
# 3. Confusion matrix
cm = build_confusion_matrix(predictions, labels)
cm_plot = Confusion.plot(cm, ["Class A", "Class B", "Class C"],
normalize: true
)
GnuplotEx.save(cm_plot, "confusion_matrix.svg")
# 4. ROC curve
{fpr, tpr, _} = ROC.calculate_curve(predictions, labels)
auc = ROC.calculate_auc(fpr, tpr)
roc_plot = ROC.plot(fpr, tpr, auc: auc)
GnuplotEx.save(roc_plot, "roc_curve.svg")
:ok
end
defp build_confusion_matrix(predictions, labels) do
# Build confusion matrix from predictions and labels
# ... implementation
end
endIntegration with Axon
If you're using Axon for neural networks, you can integrate GnuplotEx for visualization:
defmodule AxonTrainer do
require Axon
def train_with_visualization(model, train_data, val_data) do
history = %{train_loss: [], val_loss: [], train_acc: [], val_acc: []}
model
|> Axon.Loop.trainer(:mean_squared_error, Axon.Optimizers.adam(0.001))
|> Axon.Loop.metric(:accuracy)
|> Axon.Loop.handle_event(:epoch_completed, fn state ->
# Collect metrics each epoch
history = update_history(history, state)
# Live plot every 10 epochs
if rem(state.epoch, 10) == 0 do
plot_live(history)
end
{:continue, state}
end)
|> Axon.Loop.run(train_data, epochs: 100)
history
end
defp plot_live(history) do
GnuplotEx.ML.Loss.plot(history.train_loss, history.val_loss)
|> GnuplotEx.render(:svg)
|> then(fn {:ok, svg} -> File.write!("live_training.svg", svg) end)
end
endPerformance Tips
Use streams for large datasets: GnuplotEx uses streaming to handle large Nx tensors and DataFrames efficiently.
Cache rendered plots: When using LiveView, enable caching to avoid re-rendering unchanged plots:
<.live_gnuplot plot={@plot} cache={true} cache_ttl={60_000} />Batch updates: When plotting real-time data, batch multiple points before updating the plot.
Choose appropriate formats: Use
:svgfor vector graphics (web),:pngfor raster images.
Troubleshooting
"No numeric columns found" Error
Explorer DataFrames need at least one numeric column for plotting. Check your column types:
Explorer.DataFrame.dtypes(df)Nx Tensor Shape Errors
Ensure your tensor has the expected shape:
Nx.shape(tensor) # Should be {n}, {n, 2}, {n, 3}, or {rows, cols}Memory Issues with Large Datasets
For very large datasets, consider downsampling before plotting:
# Downsample Nx tensor
tensor
|> Nx.to_list()
|> Enum.take_every(10)
|> Nx.tensor()
# Downsample DataFrame
df
|> Explorer.DataFrame.sample(1000)