diff --git a/bins/inbreeding/README.md b/bins/inbreeding/README.md
index 08111b35fe79d23fda792094b60462b4ef430b80..4519a70cea276e8ea453976b6c57897da53e5718 100644
--- a/bins/inbreeding/README.md
+++ b/bins/inbreeding/README.md
@@ -34,5 +34,8 @@ inbreeding build
 ```
 ```bash
 inbreeding run --options $OPTS --prng-seed $PRNG_SEED
-inbreeding plot ... --options { k: $OPTS.K }
+```
+```bash
+let experiment = $"($PRNG_SEED)-($OPTS.environment)"
+inbreeding load $experiment | inbreeding plot --options { k: $OPTS.k }
 ```
diff --git a/bins/inbreeding/load.nu b/bins/inbreeding/load.nu
new file mode 100644
index 0000000000000000000000000000000000000000..7f79a9498232fbf2b7e31286756d9b7c5a88f730
--- /dev/null
+++ b/bins/inbreeding/load.nu
@@ -0,0 +1,62 @@
+use consts.nu
+
+def get-experiments []: nothing -> list<string> {
+    $consts.CACHE
+        | path join '*' '*' '*'
+        | into glob
+        | ls $in
+        | get name
+        | path split
+        | each { last 3 | reject 1 | str join "-" }
+        | uniq
+}
+
+export def main [
+    experiment: string@get-experiments, # something of the form '<seed>-<env>'
+]: nothing -> table<strategy: string, diversity: table<x: int, y: float>> {
+    let exp = $experiment | parse "{seed}-{env}" | into record
+    if $exp == {} {
+        error throw {
+            err: "invalid experiment",
+            label: $"should have format '<seed>-<env>', found ($experiment)",
+            span: (metadata $experiment).span,
+        }
+    }
+
+    let experiment_path = [$consts.CACHE, $exp.seed, '*', $exp.env, '*' ]
+        | path join
+        | into glob
+    let experiment_files = try {
+        ls $experiment_path
+    } catch {
+        error throw {
+            err: "invalid experiment",
+            label: $"experiment '($experiment)' does not appear to have data files",
+            span: (metadata $experiment).span,
+        }
+    }
+
+    $experiment_files
+        | insert strategy { get name | path split | last }
+        | select name strategy
+        | insert diversity {
+            ls $in.name
+                | each { get name | open | lines }
+                | flatten
+                | parse "{x}, {y}"
+                | into float y
+                | group-by x --to-table
+                | update items { get y | math avg }
+                | rename --column { group: "x", items: "y" }
+                | into int x # NOTE: $.x needs to be converted to int here because
+                             # `group-by --to-table` converts the grouping key to
+                             # string
+        }
+        | reject name
+        | group-by strategy --to-table
+        | update items {|it|
+            let d = $it.items.diversity
+            $d | skip 1 | reduce --fold $d.0 {|it, acc| $acc | append $it}
+        }
+        | rename --column { group: "strategy", items: "diversity" }
+}
diff --git a/bins/inbreeding/mod.nu b/bins/inbreeding/mod.nu
index 658b9a085ad5dff367fe74586db9d50fd593f6c5..6917cad337879d3b0d53c99d2a17b3a2d1b81720 100644
--- a/bins/inbreeding/mod.nu
+++ b/bins/inbreeding/mod.nu
@@ -1,3 +1,4 @@
 export use build.nu
 export use run.nu
+export use load.nu
 export use plot.nu
diff --git a/bins/inbreeding/plot.nu b/bins/inbreeding/plot.nu
index ec82be6295845000ee27dd5165f22887e73894da..11ca0f71adf6eaa0223c2094ce10a55bc0958169 100644
--- a/bins/inbreeding/plot.nu
+++ b/bins/inbreeding/plot.nu
@@ -3,6 +3,7 @@ use std repeat
 use consts.nu
 use ../../.nushell/plot.nu gplt
 use ../../.nushell/color.nu *
+use ../../.nushell/error.nu "error throw"
 
 def "parse strategy" []: string -> record<type: string> {
     let s = $in
@@ -39,41 +40,11 @@ def get-color []: int -> string {
     }
 }
 
-def get-experiments []: nothing -> list<string> {
-    $consts.CACHE
-        | path join '*' '*' '*'
-        | into glob
-        | ls $in
-        | get name
-        | path split
-        | each { last 3 | str join "-" }
-}
-
 export def main [
-    experiment: string@get-experiments, # something of the form '<seed>-<timestamp>-<env>'
     --save: path,
     --options: record<k: int>
-] {
-    let data = [$consts.CACHE, ($experiment | str replace --all '-' (char path_sep)), '*' ]
-        | path join
-        | into glob
-        | ls $in
-        | insert strategy { get name | path split | last }
-        | select name strategy
-        | insert diversity {
-            ls $in.name
-                | each { get name | open | lines }
-                | flatten
-                | parse "{x}, {y}"
-                | into float y
-                | group-by x --to-table
-                | update items { get y | math avg }
-                | rename --column { group: "x", items: "y" }
-                | into int x # NOTE: $.x needs to be converted to int here because
-                             # `group-by --to-table` converts the grouping key to
-                             # string
-        }
-        | reject name
+]: table<strategy: string, diversity: table<x: int, y: float>> -> nothing {
+    let data = $in
     let l = $data.diversity.0 | length
 
     $data