Generating table-like x-axis tick labels

When plotting experimental data, it is common to generate categorical plots organized by condition. However, many times the full conditions are combinations of values for several different parameters. This can result in very long condition names that are difficult to read, and rotating the labels does not always help. Alternatively, conditions may be labeled with a short code (e.g., "Condition 1" or "A+B"). This is less intuitive to understand, as it requires cross-referencing with a legend.

../_images/bad-formatting-examples.svg

A better solution would concisely communicate conditions based on its value of each relevant parameter (or metadata). A table-like format for the axis label is well-suited to achieve this.

../_images/desired-output.svg

So, we’d like to convert default x-axis tick labels to custom ones, with each metadata value on a separate line. We’d also like to do this automatically to ensure that the new labels line up correctly with their corresponding condition, and to take advantage of any metadata already included in our data.

We can use generate_xticklabels to do this:

rushd.plot.generate_xticklabels(df_labels, ax_col, label_cols, *, ax=None, align_ticklabels='center', align_annotation='right', linespacing=1.2)[source]

Create table-like x-axis tick labels based on provided metadata.

Parameters:
  • df_labels (Pandas DataFrame) – DataFrame of metadata related to original xticklabels. Columns are metadata values, including the x-axis value.

  • ax_col – Column of ‘df_labels’ that contains the original xticklabels. For seaborn plots, this should be equivalent to the column passed to the x variable.

  • label_cols (List) – List of columns of ‘df_labels’ to use to replace the xticklabels.

  • ax (Optional Matplotlib axes) – Axes to edit, uses current axes if none specified.

  • align_ticklabels ('left', 'center', or 'right') – Text alignment for multi-line xticklabels.

  • align_annotation ('left', 'center', or 'right') – Text alignment for multi-line annotations comprising the columns of ‘df_labels’ used to replace the xticklabels. These appear to the bottom left of the plot, with the bounding box right-aligned with the right of the yticklabels and aligned vertically center with the vertical center of the xticklabels.

  • linespacing (float) – Spacing between rows of the new xticklabels, as a multiple of the font size. Keeps matplotlib default (1.2) if not specified.

Return type:

None; modifies the axes in place.

Consider an example with two cell lines and four conditions containing the presence or absence of each of two small molecules:

data = pd.DataFrame(
    {
        "condition": ["A", "B", "C", "D"] * 2,
        "cell_type": ["MEF"] * 4 + ["iMN"] * 4,
        "data": [1, 3, 2, 4, 2, 2, 4, 5],
        "doxycycline": ["–", "+", "–", "+"] * 2,
        "guanine": ["–", "–", "+", "+"] * 2,
    }
)
data["name"] = data.cell_type + data.condition
fig, ax = plt.subplots(1, 1, figsize=(6, 4))
ax.bar(data.name, data.data)

rd.plot.generate_xticklabels(data, "name", ["cell_type", "doxycycline", "guanine"])
../_images/main-example.svg

You can also customize the text alignment using the align_annotation and align_ticklabels parameters:

rd.plot.generate_xticklabels(
    data,
    "name",
    ["cell_type", "doxycycline", "guanine"],
    align_annotation="center",
    align_ticklabels="left",
)
../_images/custom-alignment.svg

Notice that this integrates with Seaborn plots:

import seaborn as sns

g = sns.catplot(
    data=data, x="condition", y="data", col="cell_type", kind="bar", height=4, aspect=0.7
)
ax_col = "condition"
label_cols = ["doxycycline", "guanine"]
df_labels = data.drop_duplicates([ax_col] + label_cols)

rd.plot.generate_xticklabels(df_labels, ax_col, label_cols, ax=g.axes_dict["MEF"])
../_images/seaborn-usage.svg

Note

If creating multi-panel figures in Seaborn, either:

  • Set sharex=True and call generate_xticklabels on one set of axes

  • Set sharex=False and call generate_xticklabels for each set of axes