Created
March 17, 2026 13:22
-
-
Save mtorchiano/c490f4b7b91255c12c4aa9f15733570f to your computer and use it in GitHub Desktop.
Plotnine extension for likert-like plotting using divergent bar charts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from plotnine import * | |
| import pandas as pd | |
| class geom_likert(geom_col): | |
| def __init__(self, **kwargs): | |
| super().__init__(**kwargs) | |
| def __radd__(self, other): | |
| if isinstance(other, ggplot): | |
| df = other.data | |
| mapping = other.mapping | |
| item = mapping["x"] | |
| response = mapping["fill"] | |
| df_counts = df.groupby(item)[response].value_counts().reset_index(name="count") | |
| likert_options = df[response].cat.categories.tolist() | |
| if len(likert_options)%2 == 1: | |
| neutral_option = likert_options[len(likert_options) // 2] | |
| neutral_mask = df_counts[response] == neutral_option | |
| df_counts["count"] = df_counts["count"].astype(float) | |
| df_counts.loc[neutral_mask, "count"] = df_counts.loc[neutral_mask, "count"] / 2 | |
| df_counts = pd.concat([df_counts, df_counts[neutral_mask].assign(count=lambda d: -d["count"])], ignore_index=True) | |
| negative_threshold = likert_options[len(likert_options) // 2 -1] | |
| df_counts.loc[df_counts[response] <= negative_threshold, "count"] = -df_counts["count"] | |
| df_counts["prop"] = df_counts["count"] / df_counts.groupby(item)["count"].transform(lambda x: x.abs().sum()) | |
| other += geom_col(aes(y="prop"),position=position_stack(reverse=True), | |
| data=df_counts[df_counts["count"] > 0]) | |
| other += geom_col(aes(y="prop"),position=position_stack(), | |
| data=df_counts[df_counts["count"] < 0]) | |
| other += scale_y_continuous(labels=lambda l: ["{:.0f}%".format(abs(v) * 100) for v in l], limits=(-1, 1)) | |
| other += geom_hline(yintercept=0, color="gray", size=0.5, linetype="dashed") | |
| other += coord_flip() | |
| return other | |
| return NotImplemented |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment