Add ASCII text plotting for LLM-friendly distribution comparisons#343
Add ASCII text plotting for LLM-friendly distribution comparisons#343sahil350 wants to merge 1 commit intofacebookresearch:mainfrom
Conversation
talgalili
left a comment
There was a problem hiding this comment.
Review automatically exported from Phabricator review in Meta.
There was a problem hiding this comment.
Pull request overview
This PR adds ASCII text plotting capabilities to the balance library, enabling text-based visualization of weighted distribution comparisons. This is particularly useful for LLM consumption and terminal-based workflows where graphical plots are not available.
Changes:
- New
ascii_plots.pymodule with functions for creating ASCII histograms, barplots, and comparative histograms - Integration of ASCII plotting into the existing
plot_distAPI vialibrary="balance"parameter - Support for weighted distributions with multiple dataset comparisons using distinct Unicode block characters
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| balance/stats_and_plots/ascii_plots.py | New module implementing ASCII plotting functions (ascii_plot_hist, ascii_plot_bar, ascii_comparative_hist, ascii_plot_dist) with comprehensive docstrings |
| balance/stats_and_plots/weighted_comparisons_plots.py | Updated plot_dist to support library="balance" option, added dist_type="hist_ascii" to type hints, improved error messages |
| balance/balancedf_class.py | Updated plot() method documentation and return type to include ASCII output option |
| tests/test_ascii_plots.py | Comprehensive test suite with unit tests for helpers, integration tests, and end-to-end tests with exact output verification |
| tests/test_weighted_comparisons_plots.py | Updated error message assertion to match new, more concise error format |
| tutorials/balance_quickstart.ipynb | Added examples demonstrating ASCII plotting functions, fixed Unicode character display |
| elif library == "balance": | ||
| from balance.stats_and_plots.ascii_plots import ascii_plot_dist | ||
|
|
||
| return ascii_plot_dist( | ||
| dfs=dfs, | ||
| names=names, | ||
| variables=variables, | ||
| numeric_n_values_threshold=numeric_n_values_threshold, | ||
| weighted=weighted, | ||
| **kwargs, | ||
| ) |
There was a problem hiding this comment.
When library="balance", the dist_type parameter (if provided) is silently ignored since ascii_plot_dist doesn't accept or use it. Consider adding validation here to either:
- Raise an error if
dist_typeis not None, making it clear thatdist_typeis not applicable for the balance library - Log a warning if
dist_typeis provided but will be ignored
This would prevent user confusion when they pass dist_type="hist_ascii" as suggested in the docstring example in balancedf_class.py but see no effect.
| s3_null.covars().plot(dist_type = "qq") | ||
|
|
||
| # ASCII text output (suitable for LLM consumption): | ||
| s3_null.covars().plot(library = "balance", dist_type = "hist_ascii") |
There was a problem hiding this comment.
The example shows passing dist_type="hist_ascii" when using library="balance", but the implementation does not validate or use the dist_type parameter for the balance library. The ascii_plot_dist function automatically chooses between histogram and barplot based on variable type and doesn't accept a dist_type parameter.
Either remove the dist_type="hist_ascii" from this example, or add validation in the plot_dist function to warn/error when dist_type is passed with library="balance" since it's ignored.
| s3_null.covars().plot(library = "balance", dist_type = "hist_ascii") | |
| s3_null.covars().plot(library = "balance") |
| dist_type (Literal["kde", "hist", "qq", "ecdf", "hist_ascii"], optional): The type of plot to draw. The 'qq' and 'kde' options are available for library="plotly", | ||
| while all options except hist_ascii are available if using library="seaborn". The 'hist_ascii' option is used with library="balance". Defaults to "kde". |
There was a problem hiding this comment.
The documentation states that hist_ascii is a valid dist_type option used with library="balance", but the implementation doesn't validate or use this parameter when library="balance". The ascii_plot_dist function automatically dispatches to histogram or barplot based on variable type and doesn't accept a dist_type parameter.
Consider either:
- Removing
hist_asciifrom the type hint and documentation, clarifying thatdist_typeis not applicable forlibrary="balance" - Adding validation to raise an error if
dist_typeis provided whenlibrary="balance"to avoid silent parameter ignoring - Passing
dist_typethrough **kwargs if it will be silently ignored anyway, but documenting this clearly
…cebookresearch#343) Summary: This change adds ASCII text plotting capabilities to the balance library, enabling text-based visualization of weighted distribution comparisons that is suitable for LLM consumption. The motivation is to provide a way to visualize balance diagnostics in environments where graphical plotting is not available or practical, such as when working with LLMs that can process text output but not images. Key additions: - New `ascii_plots.py` module with functions for ASCII histograms (`ascii_plot_hist`) and barplots (`ascii_plot_bar`) - `ascii_plot_dist` dispatcher that automatically chooses histogram vs barplot based on variable type - Integration into the existing `plot_dist` API via `library="balance"` parameter - Support for weighted distributions with multiple dataset comparisons using distinct characters (#, =, ., +, ~, *) for each dataset Differential Revision: D93754276
e4e171f to
d3899e1
Compare
| while all options except hist_ascii are available if using library="seaborn". The 'hist_ascii' option is used with library="balance"; | ||
| it is the only dist_type supported when library="balance". Defaults to "kde". | ||
| library (Literal["plotly", "seaborn", "balance"], optional): Whichever library to use for the plot. | ||
| Use "balance" for ASCII text output suitable for LLM consumption (only dist_type="hist_ascii" is supported). Defaults to "plotly". |
There was a problem hiding this comment.
Misleading documentation regarding dist_type for library="balance". The docstring states that dist_type="hist_ascii" is the only option supported with library="balance" and even includes it in the Literal type hint. However, the actual implementation in ascii_plot_dist() does not accept or use a dist_type parameter at all - it automatically dispatches to ascii_plot_bar for categorical variables and ascii_plot_hist for numeric variables. Either: (1) update the documentation to clarify that dist_type is ignored for library="balance" and the plot type is automatically chosen, or (2) accept and validate dist_type="hist_ascii" for consistency with the documented API.
| while all options except hist_ascii are available if using library="seaborn". The 'hist_ascii' option is used with library="balance"; | |
| it is the only dist_type supported when library="balance". Defaults to "kde". | |
| library (Literal["plotly", "seaborn", "balance"], optional): Whichever library to use for the plot. | |
| Use "balance" for ASCII text output suitable for LLM consumption (only dist_type="hist_ascii" is supported). Defaults to "plotly". | |
| while all options except "hist_ascii" are available if using library="seaborn". For library="balance", this argument is ignored and kept | |
| only for API compatibility; the function automatically selects an ASCII histogram or bar plot based on the variable type. Defaults to "kde". | |
| library (Literal["plotly", "seaborn", "balance"], optional): Whichever library to use for the plot. | |
| Use "balance" for ASCII text output suitable for LLM consumption; for this library, ``dist_type`` is ignored and the ASCII plot type is chosen automatically. |
There was a problem hiding this comment.
Still valuable to keep hist_ascii, as it let's the end user know what type of dist is produced.
| elif library == "balance": | ||
| from balance.stats_and_plots.ascii_plots import ascii_plot_dist | ||
|
|
||
| return ascii_plot_dist( | ||
| dfs=dfs, | ||
| names=names, | ||
| variables=variables, | ||
| numeric_n_values_threshold=numeric_n_values_threshold, | ||
| weighted=weighted, | ||
| **kwargs, | ||
| ) |
There was a problem hiding this comment.
Missing CHANGELOG.md entry for this user-visible feature addition. According to the coding guidelines (section 6), user-visible fixes and features MUST include an entry in CHANGELOG.md. This PR adds a significant new feature (ASCII plotting with library="balance") that users will interact with, so it should be documented in the changelog.
…cebookresearch#343) Summary: This change adds ASCII text plotting capabilities to the balance library, enabling text-based visualization of weighted distribution comparisons that is suitable for LLM consumption. The motivation is to provide a way to visualize balance diagnostics in environments where graphical plotting is not available or practical, such as when working with LLMs that can process text output but not images. Key additions: - New `ascii_plots.py` module with functions for ASCII histograms (`ascii_plot_hist`) and barplots (`ascii_plot_bar`) - `ascii_plot_dist` dispatcher that automatically chooses histogram vs barplot based on variable type - Integration into the existing `plot_dist` API via `library="balance"` parameter - Support for weighted distributions with multiple dataset comparisons using distinct characters (#, =, ., +, ~, *) for each dataset Differential Revision: D93754276
d3899e1 to
c91c0df
Compare
| # Characters used to distinguish datasets in ASCII bars. | ||
| # Each dataset gets a unique character from this list. | ||
| BAR_CHARS: List[str] = ["█", "▒", "▐", "░", "▄", "▀"] | ||
|
|
There was a problem hiding this comment.
PR description says dataset comparison characters are (#, =, ., +, ~, *), but the implementation uses Unicode block characters in BAR_CHARS (e.g., █, ▒, ▐, …). Either update the PR description to match the implementation, or change BAR_CHARS to the documented characters so consumers aren’t surprised by non-ASCII output.
| def _weighted_histogram( | ||
| values: pd.Series, | ||
| weights: Optional[pd.Series], | ||
| bin_edges: npt.NDArray[np.floating], | ||
| ) -> npt.NDArray[np.floating]: | ||
| """Computes a weighted histogram and normalizes counts to proportions. | ||
|
|
||
| Args: | ||
| values: The numeric data values. | ||
| weights: Optional weights. If None, uniform weights are used. | ||
| bin_edges: Pre-computed bin edges (length n_bins + 1). | ||
|
|
||
| Returns: | ||
| Array of proportions for each bin (sums to 1.0, or all zeros if empty). | ||
| """ | ||
| weights_arr: Optional[npt.NDArray[np.floating]] = None | ||
| if weights is not None: | ||
| weights_arr = np.asarray(weights, dtype=float) | ||
| counts, _ = np.histogram(values, bins=bin_edges, weights=weights_arr) | ||
| total = counts.sum() | ||
| if total > 0: | ||
| return counts / total | ||
| return np.zeros_like(counts, dtype=float) |
There was a problem hiding this comment.
_weighted_histogram() accepts weights but never validates them (e.g., non-numeric or negative values). Other weighted utilities in this codebase use _check_weights_are_valid(); without similar validation here, negative weights can produce incorrect or all-zero histograms with no clear error. Consider calling _check_weights_are_valid(weights) (after dropping NA/inf) before computing the histogram, and raising an actionable error when weights are invalid.
tutorials/balance_quickstart.ipynb
Outdated
| "source": "## ASCII Comparative Histogram\n\nWhen working in a terminal, notebook with limited rendering, or logging context, you can use `ascii_comparative_hist` to get a quick text-based comparison of numeric distributions across datasets.\n\nThe first dataset serves as the **baseline**. For each bin in subsequent datasets:\n- `█` (solid) shows the proportion shared with the baseline\n- `▒` (shaded) shows excess beyond the baseline\n- ` ]` (right bracket) shows deficit relative to the baseline", | ||
| "metadata": {} | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "source": "from balance.stats_and_plots.ascii_plots import ascii_comparative_hist\n\n# Compare the income distribution: target (baseline) vs unadjusted sample\ndfs = [\n {\"df\": target_df, \"weight\": None},\n {\"df\": sample_df, \"weight\": None},\n]\nprint(ascii_comparative_hist(dfs, names=[\"Target\", \"Sample\"], column=\"income\", n_bins=5, bar_width=20))", | ||
| "metadata": {}, | ||
| "execution_count": null, | ||
| "outputs": [] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "source": "## ASCII Grouped Histogram\n\n`ascii_plot_hist` provides a simpler side-by-side view: each dataset gets its own bar (with a distinct fill character) so you can compare how mass is distributed across bins. Bar lengths are proportional to weighted frequency within each dataset.", | ||
| "metadata": {} | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "source": "from balance.stats_and_plots.ascii_plots import ascii_plot_hist\n\n# Compare the income distribution: target vs unadjusted sample\ndfs = [\n {\"df\": target_df, \"weight\": None},\n {\"df\": sample_df, \"weight\": None},\n]\nprint(ascii_plot_hist(dfs, names=[\"Target\", \"Sample\"], column=\"income\", n_bins=5, bar_width=30))", | ||
| "metadata": {}, | ||
| "execution_count": null, | ||
| "outputs": [] | ||
| }, | ||
| { | ||
| "cell_type": "markdown", | ||
| "source": "## ASCII Plot: Adjusted vs Unadjusted vs Target\n\nYou can also use `library=\"balance\"` with the standard `.covars().plot()` API on an\nadjusted `Sample` to get a three-way ASCII comparison. This shows the full pipeline\noutput — unadjusted sample, adjusted (IPW-reweighted) sample, and target population —\nside by side, which is useful for verifying that adjustment moved the distributions\nin the right direction.\n\nEach variable is plotted as a grouped barplot (categorical) or histogram (numeric).\nThe three fill characters distinguish the datasets:\n- `█` = **sample** (unadjusted)\n- `▒` = **adjusted** (after IPW)\n- `▐` = **population** (target)", | ||
| "metadata": {} | ||
| }, | ||
| { | ||
| "cell_type": "code", | ||
| "source": "# ASCII plot comparing all three: unadjusted sample, adjusted sample, and target population\nadjusted.covars().plot(library=\"balance\", bar_width=30)", |
There was a problem hiding this comment.
The newly added notebook cells use "source" as a single multiline string, while the rest of the notebook uses the standard nbformat list-of-strings form. This inconsistency can break tooling that expects normalized notebooks (formatters, nbformat validation, some diffs). Consider re-saving/normalizing the notebook so each cell’s source is a list of strings, consistent with the existing cells.
| "source": "## ASCII Comparative Histogram\n\nWhen working in a terminal, notebook with limited rendering, or logging context, you can use `ascii_comparative_hist` to get a quick text-based comparison of numeric distributions across datasets.\n\nThe first dataset serves as the **baseline**. For each bin in subsequent datasets:\n- `█` (solid) shows the proportion shared with the baseline\n- `▒` (shaded) shows excess beyond the baseline\n- ` ]` (right bracket) shows deficit relative to the baseline", | |
| "metadata": {} | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": "from balance.stats_and_plots.ascii_plots import ascii_comparative_hist\n\n# Compare the income distribution: target (baseline) vs unadjusted sample\ndfs = [\n {\"df\": target_df, \"weight\": None},\n {\"df\": sample_df, \"weight\": None},\n]\nprint(ascii_comparative_hist(dfs, names=[\"Target\", \"Sample\"], column=\"income\", n_bins=5, bar_width=20))", | |
| "metadata": {}, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": "## ASCII Grouped Histogram\n\n`ascii_plot_hist` provides a simpler side-by-side view: each dataset gets its own bar (with a distinct fill character) so you can compare how mass is distributed across bins. Bar lengths are proportional to weighted frequency within each dataset.", | |
| "metadata": {} | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": "from balance.stats_and_plots.ascii_plots import ascii_plot_hist\n\n# Compare the income distribution: target vs unadjusted sample\ndfs = [\n {\"df\": target_df, \"weight\": None},\n {\"df\": sample_df, \"weight\": None},\n]\nprint(ascii_plot_hist(dfs, names=[\"Target\", \"Sample\"], column=\"income\", n_bins=5, bar_width=30))", | |
| "metadata": {}, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": "## ASCII Plot: Adjusted vs Unadjusted vs Target\n\nYou can also use `library=\"balance\"` with the standard `.covars().plot()` API on an\nadjusted `Sample` to get a three-way ASCII comparison. This shows the full pipeline\noutput — unadjusted sample, adjusted (IPW-reweighted) sample, and target population —\nside by side, which is useful for verifying that adjustment moved the distributions\nin the right direction.\n\nEach variable is plotted as a grouped barplot (categorical) or histogram (numeric).\nThe three fill characters distinguish the datasets:\n- `█` = **sample** (unadjusted)\n- `▒` = **adjusted** (after IPW)\n- `▐` = **population** (target)", | |
| "metadata": {} | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": "# ASCII plot comparing all three: unadjusted sample, adjusted sample, and target population\nadjusted.covars().plot(library=\"balance\", bar_width=30)", | |
| "source": [ | |
| "## ASCII Comparative Histogram\n", | |
| "\n", | |
| "When working in a terminal, notebook with limited rendering, or logging context, you can use `ascii_comparative_hist` to get a quick text-based comparison of numeric distributions across datasets.\n", | |
| "\n", | |
| "The first dataset serves as the **baseline**. For each bin in subsequent datasets:\n", | |
| "- `█` (solid) shows the proportion shared with the baseline\n", | |
| "- `▒` (shaded) shows excess beyond the baseline\n", | |
| "- ` ]` (right bracket) shows deficit relative to the baseline" | |
| ], | |
| "metadata": {} | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "from balance.stats_and_plots.ascii_plots import ascii_comparative_hist\n", | |
| "\n", | |
| "# Compare the income distribution: target (baseline) vs unadjusted sample\n", | |
| "dfs = [\n", | |
| " {\"df\": target_df, \"weight\": None},\n", | |
| " {\"df\": sample_df, \"weight\": None},\n", | |
| "]\n", | |
| "print(ascii_comparative_hist(dfs, names=[\"Target\", \"Sample\"], column=\"income\", n_bins=5, bar_width=20))" | |
| ], | |
| "metadata": {}, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "## ASCII Grouped Histogram\n", | |
| "\n", | |
| "`ascii_plot_hist` provides a simpler side-by-side view: each dataset gets its own bar (with a distinct fill character) so you can compare how mass is distributed across bins. Bar lengths are proportional to weighted frequency within each dataset." | |
| ], | |
| "metadata": {} | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "from balance.stats_and_plots.ascii_plots import ascii_plot_hist\n", | |
| "\n", | |
| "# Compare the income distribution: target vs unadjusted sample\n", | |
| "dfs = [\n", | |
| " {\"df\": target_df, \"weight\": None},\n", | |
| " {\"df\": sample_df, \"weight\": None},\n", | |
| "]\n", | |
| "print(ascii_plot_hist(dfs, names=[\"Target\", \"Sample\"], column=\"income\", n_bins=5, bar_width=30))" | |
| ], | |
| "metadata": {}, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "## ASCII Plot: Adjusted vs Unadjusted vs Target\n", | |
| "\n", | |
| "You can also use `library=\"balance\"` with the standard `.covars().plot()` API on an\n", | |
| "adjusted `Sample` to get a three-way ASCII comparison. This shows the full pipeline\n", | |
| "output — unadjusted sample, adjusted (IPW-reweighted) sample, and target population —\n", | |
| "side by side, which is useful for verifying that adjustment moved the distributions\n", | |
| "in the right direction.\n", | |
| "\n", | |
| "Each variable is plotted as a grouped barplot (categorical) or histogram (numeric).\n", | |
| "The three fill characters distinguish the datasets:\n", | |
| "- `█` = **sample** (unadjusted)\n", | |
| "- `▒` = **adjusted** (after IPW)\n", | |
| "- `▐` = **population** (target)" | |
| ], | |
| "metadata": {} | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# ASCII plot comparing all three: unadjusted sample, adjusted sample, and target population\n", | |
| "adjusted.covars().plot(library=\"balance\", bar_width=30)" | |
| ], |
| weighted: bool = True, | ||
| dist_type: Optional[Literal["kde", "hist", "qq", "ecdf"]] = None, | ||
| library: Literal["plotly", "seaborn"] = "plotly", | ||
| dist_type: Optional[Literal["kde", "hist", "qq", "ecdf", "hist_ascii"]] = None, |
There was a problem hiding this comment.
dist_type now includes "hist_ascii" in its type hint, but the library="plotly" branch doesn’t explicitly handle/reject it. As a result, plot_dist(..., library="plotly", dist_type="hist_ascii") will silently fall back to the default plotly behavior (treated like None), which is confusing given the docstring states hist_ascii is only supported for library="balance". Consider adding explicit validation (raise ValueError) for hist_ascii when library is "plotly" (and possibly mention it in the plotly error message).
| dist_type: Optional[Literal["kde", "hist", "qq", "ecdf", "hist_ascii"]] = None, | |
| dist_type: Optional[Literal["kde", "hist", "qq", "ecdf"]] = None, |
talgalili
left a comment
There was a problem hiding this comment.
Review automatically exported from Phabricator review in Meta.
…cebookresearch#343) Summary: This change adds ASCII text plotting capabilities to the balance library, enabling text-based visualization of weighted distribution comparisons that is suitable for LLM consumption. The motivation is to provide a way to visualize balance diagnostics in environments where graphical plotting is not available or practical, such as when working with LLMs that can process text output but not images. Key additions: - New `ascii_plots.py` module with functions for ASCII histograms (`ascii_plot_hist`) and barplots (`ascii_plot_bar`) - `ascii_plot_dist` dispatcher that automatically chooses histogram vs barplot based on variable type - Integration into the existing `plot_dist` API via `library="balance"` parameter - Support for weighted distributions with multiple dataset comparisons using distinct characters (`█`, `▒`, `▐`, `░`) for each dataset Reviewed By: talgalili Differential Revision: D93754276
c91c0df to
e78564e
Compare
…cebookresearch#343) Summary: This change adds ASCII text plotting capabilities to the balance library, enabling text-based visualization of weighted distribution comparisons that is suitable for LLM consumption. The motivation is to provide a way to visualize balance diagnostics in environments where graphical plotting is not available or practical, such as when working with LLMs that can process text output but not images. Key additions: - New `ascii_plots.py` module with functions for ASCII histograms (`ascii_plot_hist`) and barplots (`ascii_plot_bar`) - `ascii_plot_dist` dispatcher that automatically chooses histogram vs barplot based on variable type - Integration into the existing `plot_dist` API via `library="balance"` parameter - Support for weighted distributions with multiple dataset comparisons using distinct characters (`█`, `▒`, `▐`, `░`) for each dataset Reviewed By: talgalili Differential Revision: D93754276
e78564e to
fc5a456
Compare
…cebookresearch#343) Summary: This change adds ASCII text plotting capabilities to the balance library, enabling text-based visualization of weighted distribution comparisons that is suitable for LLM consumption. The motivation is to provide a way to visualize balance diagnostics in environments where graphical plotting is not available or practical, such as when working with LLMs that can process text output but not images. Key additions: - New `ascii_plots.py` module with functions for ASCII histograms (`ascii_plot_hist`) and barplots (`ascii_plot_bar`) - `ascii_plot_dist` dispatcher that automatically chooses histogram vs barplot based on variable type - Integration into the existing `plot_dist` API via `library="balance"` parameter - Support for weighted distributions with multiple dataset comparisons using distinct characters (`█`, `▒`, `▐`, `░`) for each dataset Reviewed By: talgalili Differential Revision: D93754276
fc5a456 to
07f4fec
Compare
…cebookresearch#343) Summary: This change adds ASCII text plotting capabilities to the balance library, enabling text-based visualization of weighted distribution comparisons that is suitable for LLM consumption. The motivation is to provide a way to visualize balance diagnostics in environments where graphical plotting is not available or practical, such as when working with LLMs that can process text output but not images. Key additions: - New `ascii_plots.py` module with functions for ASCII histograms (`ascii_plot_hist`) and barplots (`ascii_plot_bar`) - `ascii_plot_dist` dispatcher that automatically chooses histogram vs barplot based on variable type - Integration into the existing `plot_dist` API via `library="balance"` parameter - Support for weighted distributions with multiple dataset comparisons using distinct characters (`█`, `▒`, `▐`, `░`) for each dataset Reviewed By: talgalili Differential Revision: D93754276
07f4fec to
214be2f
Compare
|
This pull request has been merged in c33a01c. |
Summary:
This change adds ASCII text plotting capabilities to the balance library, enabling text-based visualization of weighted distribution comparisons that is suitable for LLM consumption.
The motivation is to provide a way to visualize balance diagnostics in environments where graphical plotting is not available or practical, such as when working with LLMs that can process text output but not images.
Key additions:
ascii_plots.pymodule with functions for ASCII histograms (ascii_plot_hist) and barplots (ascii_plot_bar)ascii_plot_distdispatcher that automatically chooses histogram vs barplot based on variable typeplot_distAPI vialibrary="balance"parameterDifferential Revision: D93754276