diff --git a/libcodonusage/__init__.py b/libcodonusage/__init__.py
index 3eed697de8eaffd4b3af6ad444367b2ab57533b7..12d83984cccec34a5caba35cc9bb9373d3cbae6c 100644
--- a/libcodonusage/__init__.py
+++ b/libcodonusage/__init__.py
@@ -1,12 +1,15 @@
 __copyright__ = "Copyright (C) 2022 Blaise Li"
 __licence__ = "GNU GPLv3"
-__version__ = 0.3
+__version__ = 0.4
 from .libcodonusage import (
     aa2colour,
+    codon2aa,
     columns_by_aa,
     detect_fishy_genes,
     load_bias_table,
     load_counts_table,
+    make_aa_codon_columns,
+    make_counts_only,
     render_md,
     violin_usage,
     violin_usage_vertical,
diff --git a/libcodonusage/libcodonusage.py b/libcodonusage/libcodonusage.py
index c577e32bc37be4af84fbce784b67b33b38826670..b8ee559137a1be78796f0174e2ff433e0ae026d8 100644
--- a/libcodonusage/libcodonusage.py
+++ b/libcodonusage/libcodonusage.py
@@ -224,6 +224,27 @@ def detect_fishy_genes(codon_counts):
     return criteria
 
 
+def make_counts_only(counts_table):
+    """
+    Integrate "informative" columns of *counts_table* into the index.
+    """
+    info_cols = [
+        counts_table.index.name,
+        *counts_table.columns.difference(codon2aa)]
+    assert set(info_cols) == {
+        "old_locus_tag", "locus_tag", "length", "start_codon", "expected_start_aa",
+        "first_stop", "nb_stops", "start_upstream", "end_downstream"}
+    return counts_table.reset_index().set_index(info_cols)
+
+
+def make_aa_codon_columns(counts_table):
+    """Transform column headers into a (aa, codon) MultiIndex."""
+    counts_table.columns = pd.MultiIndex.from_tuples(
+        ((codon2aa[codon], codon) for codon in counts_table.columns),
+        names=("aa", "codon"))
+    return counts_table
+
+
 def load_bias_table(table_path, nb_cluster_series=2):
     """
     Load a table containing by-amino-acid codon usage biases.