Skip to content

Repository files navigation

Forestplot

PyPI - Python Version
Easy API for forest plots.
A Python package to make publication-ready but customizable forest plots.


This package makes publication-ready forest plots easy to make out-of-the-box. Users provide a dataframe (e.g. from a spreadsheet) where rows correspond to a variable/study with columns including estimates, variable labels, and lower and upper confidence interval limits. Additional options allow easy addition of columns in the dataframe as annotations in the plot.

ReleasePyPIConda (channel only)GitHub release (latest by date)
StatusCINotebooks
CoverageCodecov
PythonPyPI - Python Version
DocsRead the Docs (version)DocLinks
MetaGitHubImports: isortCode style: blacktypes - MypyDOI
BinderBinder

Table of Contents

show/hide

Installation

Install from PyPI
PyPI

pip install forestplot

Install from conda-forge
Conda (channel only)

conda install forestplot

Install from source
GitHub release (latest by date)

git clone https://github.com/LSYS/forestplot.git
cd forestplot
pip install .

Developer installation

git clone https://github.com/LSYS/forestplot.git
cd forestplot
pip install -r requirements_dev.txt
make lint
make test

(back to top)

Quick Start

importforestplotasfpdf=fp.load_data("sleep") # companion example datadf.head(3)
varrmoerrorlabelgroupllhlnpowerp-val
0age0.09037290.0696271in yearsage0.020.167060.6715780.0163089
1black-0.02705730.0770573=1 if blackother factors-0.10.057060.1108050.472889
2clerical0.04808110.0719189=1 if clerical workeroccupation-0.030.127060.2477680.201948

(* This is a toy example of how certain factors correlate with the amount of sleep one gets. See the notebook that generates the data.)

The example input dataframe above have 4 key columns
ColumnDescriptionRequired
varVariable label
rCorrelation coefficients (estimates to plot)
labelVariable labels
groupVariable grouping labels
llConf. int. lower limits
hlContaining the conf. int. higher limits
nSample size
powerStatistical power
p-valP-value

(See Gallery and API Options for more details on required and optional arguments.)

Make the forest plot

fp.forestplot(df, # the dataframe with results dataestimate="r", # col containing estimated effect size ll="ll", hl="hl", # columns containing conf. int. lower and higher limitsvarlabel="label", # column containing variable labelylabel="Confidence interval", # y-label titlexlabel="Pearson correlation", # x-label title
)

Save the plot

plt.savefig("plot.png", bbox_inches="tight")

(back to top)

Some Examples With Customizations

  1. Add variable groupings, add group order, and sort by estimate size.
fp.forestplot(df, # the dataframe with results dataestimate="r", # col containing estimated effect size ll="ll", hl="hl", # columns containing conf. int. lower and higher limits varlabel="label", # column containing variable labelcapitalize="capitalize", # Capitalize labelsgroupvar="group", # Add variable groupings # group orderinggroup_order=["labor factors", "occupation", "age", "health factors", "family factors", "area of residence", "other factors"],
sort=True# sort in ascending order (sorts within group if group is specified) 
)

  1. Add p-values on the right and color alternate rows gray
fp.forestplot(df, # the dataframe with results dataestimate="r", # col containing estimated effect size ll="ll", hl="hl", # columns containing conf. int. lower and higher limitsvarlabel="label", # column containing variable labelcapitalize="capitalize", # Capitalize labelsgroupvar="group", # Add variable groupings # group orderinggroup_order=["labor factors", "occupation", "age", "health factors", "family factors", "area of residence", "other factors"],
sort=True, # sort in ascending order (sorts within group if group is specified) pval="p-val", # Column of p-value to be reported on rightcolor_alt_rows=True, # Gray alternate rowsylabel="Est.(95% Conf. Int.)", # ylabel to print**{"ylabel1_size": 11} # control size of printed ylabel
)

  1. Customize annotations and make it a table
fp.forestplot(df, # the dataframe with results dataestimate="r", # col containing estimated effect size ll="ll", hl="hl", # lower & higher limits of conf. int.varlabel="label", # column containing the varlabels to be printed on far leftcapitalize="capitalize", # Capitalize labelspval="p-val", # column containing p-values to be formattedannote=["n", "power", "est_ci"], # columns to report on left of plotannoteheaders=["N", "Power", "Est. (95% Conf. Int.)"], # ^corresponding headersrightannote=["formatted_pval", "group"], # columns to report on right of plot right_annoteheaders=["P-value", "Variable group"], # ^corresponding headersxlabel="Pearson correlation coefficient", # x-label titletable=True, # Format as a table
)

  1. Strip down all bells and whistle
fp.forestplot(df, # the dataframe with results dataestimate="r", # col containing estimated effect size ll="ll", hl="hl", # lower & higher limits of conf. int.varlabel="label", # column containing the varlabels to be printed on far leftcapitalize="capitalize", # Capitalize labelsci_report=False, # Turn off conf. int. reportingflush=False, # Turn off left-flush of text**{'fontfamily': 'sans-serif'} # revert to sans-serif 
)

  1. Example with more customizations
fp.forestplot(df, # the dataframe with results dataestimate="r", # col containing estimated effect size ll="ll", hl="hl", # lower & higher limits of conf. int.varlabel="label", # column containing the varlabels to be printed on far leftcapitalize="capitalize", # Capitalize labelspval="p-val", # column containing p-values to be formattedannote=["n", "power", "est_ci"], # columns to report on left of plotannoteheaders=["N", "Power", "Est. (95% Conf. Int.)"], # ^corresponding headersrightannote=["formatted_pval", "group"], # columns to report on right of plot right_annoteheaders=["P-value", "Variable group"], # ^corresponding headersgroupvar="group", # column containing group labelsgroup_order=["labor factors", "occupation", "age", "health factors", "family factors", "area of residence", "other factors"], xlabel="Pearson correlation coefficient", # x-label titlexticks=[-.4,-.2,0, .2], # x-ticks to be printedsort=True, # sort estimates in ascending ordertable=True, # Format as a table# Additional kwargs for customizations**{"marker": "D", # set maker symbol as diamond"markersize": 35, # adjust marker size"xlinestyle": (0, (10, 5)), # long dash for x-reference line "xlinecolor": "#808080", # gray color for x-reference line"xtick_size": 12, # adjust x-ticker fontsize
} )

Annotations arguments allowed include:
  • ci_range: Confidence interval range (e.g. (-0.39 to -0.25)).
  • est_ci: Estimate and CI (e.g. -0.32(-0.39 to -0.25)).
  • formatted_pval: Formatted p-values (e.g. 0.01**).

To confirm what processed columns are available as annotations, you can do:

processed_df, ax=fp.forestplot(df, ... # other arguments herereturn_df=True# return processed dataframe with processed columns
)
processed_df.head(3)
labelgroupnrCI95%p-valBF10powervarhlllmoerrorformatted_rformatted_llformatted_hlci_rangeest_ciformatted_pvalformatted_nformatted_powerformatted_est_ciyticklabelformatted_formatted_pvalformatted_groupyticklabel2
0Mins worked per weekLabor factors706-0.321384[-0.39 -0.25]1.99409e-181.961e+151totwrk-0.25-0.390.0686165-0.32-0.39-0.25(-0.39 to -0.25)-0.32(-0.39 to -0.25)0.0***7061-0.32(-0.39 to -0.25)Mins worked per week 706 1.0 -0.32(-0.39 to -0.25)0.0***Labor factors0.0*** Labor factors
1Years of schoolingLabor factors706-0.0950039[-0.17 -0.02]0.01155151.1370.72educ-0.02-0.170.0749961-0.1-0.17-0.02(-0.17 to -0.02)-0.10(-0.17 to -0.02)0.01**7060.72-0.10(-0.17 to -0.02)Years of schooling 706 0.72 -0.10(-0.17 to -0.02)0.01**Labor factors0.01** Labor factors

(back to top)

Multi-models

For coefficient plots where each variable can have multiple estimates (each model has one).

importforestplotasfpdf_mmodel=pd.read_csv("../examples/data/sleep-mmodel.csv").query(
"model=='all' | model=='young kids'"
)
df_mmodel.head(3)
varcoefseTpvalr2adj_r2llhlmodelgrouplabel
0age0.9948891.969250.5052130.6136250.1272890.103656-2.873824.8636allagein years
3age22.63415.49531.46070.1493150.178147-0.0136188-8.3612453.6293young kidsagein years
4black-84.796682.1501-1.032220.3024540.1272890.103656-246.18676.5925allother factors=1 if black
fp.mforestplot(
dataframe=df_mmodel,
estimate="coef",
ll="ll",
hl="hl",
varlabel="label",
capitalize="capitalize",
model_col="model",
color_alt_rows=True,
groupvar="group",
table=True,
rightannote=["var", "group"],
right_annoteheaders=["Source", "Group"],
xlabel="Coefficient (95% CI)",
modellabels=["Have young kids", "Full sample"],
xticks=[-1200, -600, 0, 600],
mcolor=["#CC6677", "#4477AA"],
# Additional kwargs for customizations**{
"markersize": 30,
# override default vertical offset between models (0.0 to 1.0)"offset": 0.35, "xlinestyle": (0, (10, 5)), # long dash for x-reference line"xlinecolor": ".8", # gray color for x-reference line
},
)

Please note: This module is still experimental. See this jupyter notebook for more examples and tweaks.

Gallery and API Options

Notebooks

Check out this jupyter notebook for a gallery variations of forest plots possible out-of-the-box. The table below shows the list of arguments users can pass in. More fined-grained control for base plot options (eg font sizes, marker colors) can be inferred from the example notebook gallery.

OptionDescriptionRequired
dataframePandas dataframe where rows are variables (or studies for meta-analyses) and columns include estimated effect sizes, labels, and confidence intervals, etc.
estimateName of column in dataframe containing the estimates.
varlabelName of column in dataframe containing the variable labels (study labels if meta-analyses).
llName of column in dataframe containing the conf. int. lower limits.
hlName of column in dataframe containing the conf. int. higher limits.
logscaleIf True, make the x-axis log scale. Default is False.
capitalizeHow to capitalize strings. Default is None. One of "capitalize", "title", "lower", "upper", "swapcase".
form_ci_reportIf True (default), report the estimates and confidence interval beside the variable labels.
ci_reportIf True (default), format the confidence interval as a string.
groupvarName of column in dataframe containing the variable grouping labels.
group_orderList of group labels indicating the order of groups to report in the plot.
annoteList of columns to add as annotations on the left-hand side of the plot.
annoteheadersList of column headers for the left-hand side annotations.
rightannoteList of columns to add as annotations on the right-hand side of the plot.
right_annoteheadersList of column headers for the right-hand side annotations.
pvalName of column in dataframe containing the p-values.
starpvalIf True (default), format p-values with stars indicating statistical significance.
sortIf True, sort variables by estimate values in ascending order.
sortbyName of column to sort by. Default is estimate.
flushIf True (default), left-flush variable labels and annotations.
decimal_precisionNumber of decimal places to print. (Default = 2)
figsizeTuple indicating core figure size. Default is (4, 8)
xticksList of xticklabels to print on x-axis.
ylabelY-label title.
xlabelX-label title.
color_alt_rowsIf True, shade out alternating rows in gray.
preprocessIf True (default), preprocess the dataframe before plotting.
return_dfIf True, returned the preprocessed dataframe.

(back to top)

Known Issues

  • Variable labels coinciding with group variables may lead to unexpected formatting issues in the graph.
  • Left-flushing of annotations relies on the monospace font.
  • Plot may give strange behavior for few rows of data (six rows or fewer. see this issue)
  • Plot can get cluttered with too many variables/rows (~30 onwards)
  • Not tested with PyCharm (#80) nor Google Colab (#110).
  • Duplicated varlabel may lead to unexpected results (see #76, #81). mplot for grouped models could be useful for such cases (see #59, WIP).

(back to top)

Background and Additional Resources

More about forest plots

Forest plots have many aliases (h/t Chris Alexiuk). Other names include coefplots, coefficient plots, meta-analysis plots, dot-and-whisker plots, blobbograms, margins plots, regression plots, and ropeladder plots.

Forest plots in the medical and health sciences literature are plots that report results from different studies as a meta-analysis. Markers are centered on the estimated effect and horizontal lines running through each marker depicts the confidence intervals.

The simplest version of a forest plot has two columns: one for the variables/studies, and the second for the estimated coefficients and confidence intervals. This layout is similar to coefficient plots (coefplots) and is thus useful for more than meta-analyses.

More resources about forest plots

  • [1] Chang, Y., Phillips, M.R., Guymer, R.H. et al. The 5 min meta-analysis: understanding how to read and interpret a forest plot. Eye 36, 673–675 (2022).
  • [2] Lewis S, Clarke M. Forest plots: trying to see the wood and the trees BMJ 2001; 322 :1479

More about this package

The package is lightweight, built on pandas, numpy, and matplotlib.

It is slightly opinioniated in that the aesthetics of the plot inherits some of my sensibilities about what makes a nice figure. You can however easily override most defaults for the look of the graph. This is possible via **kwargs in the forestplot API (see Gallery and API options) and the matplotlib API.

Planned enhancements include forest plots where each row can have multiple coefficients (e.g. from multiple models).

Related packages

  • [1] [Stata] Jann, Ben (2014). Plotting regression coefficients and other estimates. The Stata Journal 14(4): 708-737.
  • [2] [Python] Meta-Analysis in statsmodels
  • [3] [Python] Matt Bracher-Smith's Forestplot
  • [4] [R] Solt, Frederick and Hu, Yue (2021) dotwhisker: Dot-and-Whisker Plots of Regression Results
  • [5] [R] Bounthavong, Mark (2021) Forest plots. RPubs by RStudio

(back to top)

Contributing

Contributions are welcome, and they are greatly appreciated!

Potential ways to contribute:

  • Raise issues/bugs/questions
  • Write tests for missing coverage
  • Add features (see examples notebook for a survey of existing features)
  • Add example datasets with companion graphs
  • Add your graphs with companion code

Issues

Please submit bugs, questions, or issues you encounter to the GitHub Issue Tracker. For bugs, please provide a minimal reproducible example demonstrating the problem (it may help me troubleshoot if I have a version of your data).

Pull Requests

Please feel free to open an issue on the Issue Tracker if you'd like to discuss potential contributions via PRs.

(back to top)

About

A Python package to make publication-ready but customizable coefficient plots.

Topics

Resources

Stars

141 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages