visual_tools
visual_tools._figure_to_inline_svg(fig)
Convert a matplotlib figure to inline SVG and close the figure.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fig
|
Matplotlib figure to render. Closed before returning, so it must not be reused afterwards. |
required |
Returns:
| Type | Description |
|---|---|
str
|
SVG markup, rendered on the report theme's page background and cropped to the figure's tight bounding box. |
visual_tools._set_white_plot_defaults()
Keep matplotlib defaults aligned with the report theme.
visual_tools._format_compact_number(value)
Format a numeric value using compact K, M, or B suffixes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
float
|
Numeric value to format. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Value scaled to one decimal place with a |
visual_tools._format_axis_ticklabels(values)
Format numeric axis values as compact labels.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
values
|
Iterable[float]
|
Numeric tick values to format. |
required |
Returns:
| Type | Description |
|---|---|
list of str
|
One compact label per value, in input order. |
visual_tools._age_sort_key(label)
Return the leading numeric value used to order age labels.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
label
|
Age label such as |
required |
Returns:
| Type | Description |
|---|---|
int
|
First integer found in the label, or |
visual_tools._normalize_age_labels(df, age_col='age')
Sort a pandas DataFrame by normalized numeric age labels.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
Input pandas DataFrame. Not modified; a copy is returned. |
required |
age_col
|
str
|
Name of the age label column, cast to string before sorting. |
'age'
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
Copy sorted by numeric age then label, with a reset index and an added
|
visual_tools._platform_label(platform_name)
Convert a machine-readable platform name into a display label.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
platform_name
|
str
|
Platform key such as |
required |
Returns:
| Type | Description |
|---|---|
str
|
Title-cased label with underscores replaced by spaces, for example
|
visual_tools._build_report_assets(include_plotly=False)
Build shared HTML, CSS, and optional Plotly assets for reports.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
include_plotly
|
bool
|
Include the Plotly script tag. Required for reports containing interactive Plotly charts. |
False
|
Returns:
| Type | Description |
|---|---|
str
|
HTML fragment with the report stylesheet and, when requested, the Plotly script tag. |
Notes
Plotly is loaded from a CDN, so reports built with include_plotly need
network access when opened.
visual_tools.html_intro(title, synpop_id)
Build the header section for a SynPop HTML report.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
title
|
str
|
Report title displayed prominently. |
required |
synpop_id
|
str
|
Synthetic-population identifier displayed as metadata. |
required |
Returns:
| Type | Description |
|---|---|
str
|
HTML |
visual_tools.svg_block(svg_str, title)
Wrap an inline SVG in a styled report section.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
svg_str
|
str
|
Inline SVG markup. |
required |
title
|
str
|
Optional visualization title. An empty value omits the heading. |
required |
Returns:
| Type | Description |
|---|---|
str
|
HTML visualization block. |
visual_tools.info_block(message_html, title='Note')
Build a styled informational block from trusted HTML content.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message_html
|
str
|
HTML inserted into the block body without escaping. |
required |
title
|
str
|
Label displayed above the message. |
'Note'
|
Returns:
| Type | Description |
|---|---|
str
|
HTML information block. |
visual_tools.population_pyramid_reference_note()
Return the standard PopulationPyramid.net reference disclaimer.
Returns:
| Type | Description |
|---|---|
str
|
HTML note explaining that the demographic pyramid is a visual |
str
|
benchmark and is not used for SynPop generation or calibration. |
visual_tools.side_by_side_blocks(left_block, right_block, left_title='Synthetic Population', right_title='Demographic Population', height_px=760, header_px=44, footer_px=28)
Lay out synthetic and demographic visualizations side by side.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
left_block
|
str
|
HTML for the synthetic-population panel. |
required |
right_block
|
str
|
HTML for the demographic reference panel. |
required |
left_title
|
str
|
Left panel heading. |
'Synthetic Population'
|
right_title
|
str
|
Right panel heading. |
'Demographic Population'
|
height_px
|
int
|
Total height of each panel in pixels. |
760
|
header_px
|
int
|
Reserved panel-header height in pixels. |
44
|
footer_px
|
int
|
Reserved panel-footer height in pixels. |
28
|
Returns:
| Type | Description |
|---|---|
str
|
Responsive two-panel HTML layout. |
visual_tools.wrap_html(blocks, page_title='Report', include_plotly=False)
Assemble report blocks into a complete standalone HTML document.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
blocks
|
list[str]
|
Ordered HTML fragments to place in the report container. |
required |
page_title
|
str
|
Browser-document title. |
'Report'
|
include_plotly
|
bool
|
If True, include Plotly assets in the report head. |
False
|
Returns:
| Type | Description |
|---|---|
str
|
Complete HTML document string with shared report styles and assets. |
visual_tools._add_response_time_minutes(df)
Add valid survey response durations in minutes to a Spark DataFrame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
Spark DataFrame containing |
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
Input with added |
visual_tools._add_age_gender_columns(df, age_col='age', gender_col='gender')
Add visual-report age/gender labels and sort keys.
This private wrapper keeps existing visual helper behavior while the shared
demographic normalization logic lives in demographics.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
Spark DataFrame containing the age and gender source columns. |
required | |
age_col
|
str
|
Name of the input age column. |
'age'
|
gender_col
|
str
|
Name of the input gender column. |
'gender'
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
Input with |
visual_tools.compute_median_and_threshold(df)
Calculate the median response time and the fast-response threshold.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
Spark DataFrame containing |
required |
Returns:
| Type | Description |
|---|---|
|
Pair |
|
|
approximate median. |
visual_tools.extract_response_time_array(df)
Collect non-null response times into a NumPy array.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
Spark DataFrame containing |
required |
Returns:
| Type | Description |
|---|---|
|
One-dimensional NumPy array collected through pandas. |
Notes
This operation collects the selected column to the driver.
visual_tools.remove_right_tail_outliers(x)
Remove values above the upper Tukey fence from an array.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Numeric NumPy-compatible array. |
required |
Returns:
| Type | Description |
|---|---|
|
Values less than or equal to |
|
|
retained. |
visual_tools.compute_histogram_and_smooth_density(x, bins=80, sigma=2.0)
Build a density histogram and Gaussian-smoothed density series.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Numeric sample array. |
required | |
bins
|
Histogram bin count or other value accepted by |
80
|
|
sigma
|
Gaussian-kernel standard deviation in grid units. |
2.0
|
Returns:
| Type | Description |
|---|---|
|
Tuple |
|
|
arrays. |
visual_tools.build_plot_figure(x, bin_centers, smooth_density, threshold, median_minutes, bins=80)
Create the response-time histogram and threshold figure.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Response-time sample array. |
required | |
bin_centers
|
X coordinates for the smoothed density line. |
required | |
smooth_density
|
Smoothed density values. |
required | |
threshold
|
Fast-response threshold in minutes. |
required | |
median_minutes
|
Median response time shown in the annotation. |
required | |
bins
|
Histogram bin count. |
80
|
Returns:
| Type | Description |
|---|---|
|
Matplotlib Figure containing the histogram, density line, threshold, |
|
|
median annotation, and legend. |
visual_tools.response_time_svg_html(df, bins=80, sigma=2.0)
Render a survey response-time distribution as inline SVG.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
Spark DataFrame containing |
required | |
bins
|
Histogram bin count. |
80
|
|
sigma
|
Gaussian smoothing strength. |
2.0
|
Returns:
| Type | Description |
|---|---|
|
Inline SVG string containing the response-time chart. |
Notes
Responses starting before 2026-01-01, negative durations, null durations, and upper-tail Tukey outliers are excluded.
visual_tools._aggregate_to_pandas(df_age_gender)
Aggregate age-gender counts and collect the small plotting table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df_age_gender
|
Spark DataFrame containing |
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
One row per age group with integer |
Notes
Collects to the driver with toPandas. The pivoted result has one row per
age group, so it stays small regardless of input size. Only M and F
labels are pivoted; other gender values are dropped.
visual_tools._build_pyramid_svg(pyramid_pd, title='Age-Gender Distribution Pyramid')
Render an aggregated age-gender population pyramid as SVG.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pyramid_pd
|
Pandas DataFrame with |
required | |
title
|
Chart title. |
'Age-Gender Distribution Pyramid'
|
Returns:
| Type | Description |
|---|---|
str
|
Inline SVG markup for the pyramid. Legend labels include each gender's total and its share of the population. |
visual_tools.age_gender_pyramid_svg_html(df, age_col='age', gender_col='gender', title='Age-Gender Distribution Pyramid (after dump)')
Render an age-and-gender population pyramid as inline SVG.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
Spark DataFrame containing age and gender values. |
required | |
age_col
|
str
|
Source age-column name. |
'age'
|
gender_col
|
str
|
Source gender-column name. |
'gender'
|
title
|
Figure title. |
'Age-Gender Distribution Pyramid (after dump)'
|
Returns:
| Type | Description |
|---|---|
|
Inline SVG string with male counts on the left and female counts on the |
|
|
right across standardized age groups. |
Notes
Aggregated counts are collected to pandas for plotting.
visual_tools._to_age_band(age)
Map an integer age to a five-year display band.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
age
|
int
|
Age in years. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Band label such as |
visual_tools._age_band_sort_key(label)
Return the lower bound used to order an age-band label.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
label
|
str
|
Band label produced by :func: |
required |
Returns:
| Type | Description |
|---|---|
int
|
Lower bound of the band, and |
visual_tools._resolve_gender_sides(gender_labels)
Choose the left and right labels for an age-gender pyramid.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gender_labels
|
list[str]
|
Exactly two gender category labels. |
required |
Returns:
| Type | Description |
|---|---|
tuple
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
visual_tools._prepare_age_gender_pivot(df, age_group_col, gender_label_col, n_col)
Validate, age-band, and pivot population counts for pyramid plotting.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
Pandas DataFrame of population counts. |
required |
age_group_col
|
str
|
Column holding a numeric age, bucketed into five-year bands. |
required |
gender_label_col
|
str
|
Column holding the gender label, pivoted into one column per label. |
required |
n_col
|
str
|
Column holding the numeric count to sum. |
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
Counts pivoted to age band by gender label, ordered by age band with missing combinations filled with zero. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If a required column is missing, or if no rows remain after dropping nulls and non-numeric ages and counts. |
visual_tools.age_gender_pyramid_html(df, age_group_col, gender_label_col, n_col, include_plotlyjs='cdn')
Build an interactive age-gender pyramid and return it as HTML.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
Pandas DataFrame of population counts. |
required |
age_group_col
|
str
|
Column holding a numeric age, bucketed into five-year bands. |
required |
gender_label_col
|
str
|
Column holding the gender label. Must contain exactly two labels. |
required |
n_col
|
str
|
Column holding the numeric count to sum. |
required |
include_plotlyjs
|
str | bool
|
How to include the Plotly library, passed through to Plotly. |
'cdn'
|
Returns:
| Type | Description |
|---|---|
str
|
HTML for the interactive pyramid. Bars show each band's share of the grand total, with the horizontal axis fixed at plus or minus 10 percent. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If a required column is missing, if no usable rows remain, if
|
visual_tools._usage_rate_expr(colname, *, null_is_valid_no=False)
Calculate the share of valid yes/no responses where the value is yes.
Survey checkbox columns use 1 for selected/used/owned. Depending on
the source path, unselected valid responses may be encoded as 0 or
-2. Other values are treated as missing/skipped and excluded from the
denominator unless null_is_valid_no is set.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
colname
|
str
|
Survey column to aggregate. The result is aliased back to this name. |
required |
null_is_valid_no
|
bool
|
Count NULL as a valid "no" response rather than excluding it from the denominator. |
False
|
Returns:
| Type | Description |
|---|---|
Column
|
Aggregate expression yielding the share of valid responses equal to
|
visual_tools._mean_score_expr(colname)
Build a Spark mean expression that excludes the missing-value sentinel.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
colname
|
Numeric survey column to average. The result is aliased back to this name. |
required |
Returns:
| Type | Description |
|---|---|
Column
|
Mean expression ignoring the |
visual_tools.age_gender_heatmap_svg_html(df, col_prefix, agg='share_used', value_fmt='{:.2f}', figsize=(14, 6))
Render prefixed metrics by age and gender as an inline SVG heatmap.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
Spark DataFrame containing age, gender, and metric columns. |
required | |
col_prefix
|
str
|
Prefix selecting metric columns. |
required |
agg
|
str
|
Aggregation mode: |
'share_used'
|
value_fmt
|
str
|
Format string for cell annotations, or None to omit values. |
'{:.2f}'
|
figsize
|
Matplotlib figure size. |
(14, 6)
|
Returns:
| Type | Description |
|---|---|
str
|
Inline SVG heatmap. If every aggregated value is null, a small |
str
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If no columns match |
Notes
share_used treats 1 as yes and 0/-2 as valid no values. Null
ownership values are also treated as valid no responses.
visual_tools._prepare_platform_usage_dataframe(df)
Collect platform usage data when necessary and order it by age.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
Pandas or Spark DataFrame containing an |
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
Copy ordered by numeric age with an added |
Notes
Spark input is collected to the driver with toPandas. The input is
expected to be a per-age aggregate rather than row-level data.
visual_tools._build_platform_usage_series(pdf, platform_name)
Build positive and remaining population series for one platform.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pdf
|
DataFrame
|
Pandas DataFrame with |
required |
platform_name
|
str
|
Platform whose frequency column supplies the positive series. |
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
Frame with |
visual_tools._style_axis_as_compact_numbers(ax)
Replace an axis's y-axis labels with compact numeric labels.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ax
|
Matplotlib axis to restyle in place. |
required |
Notes
Fixes the current tick positions before relabeling, so the labels stay aligned with the ticks.
visual_tools._render_platform_usage_chart(platform_df, platform_name, country_code)
Render one age-based platform-usage chart as an HTML/SVG block.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
platform_df
|
DataFrame
|
Frame from :func: |
required |
platform_name
|
str
|
Platform being charted. Must be a key of |
required |
country_code
|
str
|
Country code included in the block title. |
required |
Returns:
| Type | Description |
|---|---|
str
|
HTML block containing the stacked platform-usage chart, with users shown against the total population per age. |
visual_tools.platform_usage_per_age(df, country_code)
Create per-platform age-distribution visualization blocks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
Pandas or Spark DataFrame containing |
required | |
country_code
|
str
|
Country code included in chart titles. |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
List of HTML/SVG blocks, one for each configured platform column found |
list[str]
|
in the input. |
Notes
Spark input is collected to pandas before plotting.
visual_tools.plot_bar_chart_from_columns(df)
Render labeled values as a descending inline SVG bar chart.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
Pandas DataFrame containing |
required |
Returns:
| Type | Description |
|---|---|
str
|
Inline SVG bar chart using configured platform colors and compact |
str
|
numeric labels. |
visual_tools.embed_from_population_pyramid_net(country_code, year=2026, size_px=None)
Build a responsive PopulationPyramid.net iframe embed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
country_code
|
str
|
Two-letter country code. Common codes are mapped to the |
required |
year
|
int
|
Population-pyramid year. |
2026
|
size_px
|
int | None
|
Optional square chart size. Defaults to the report panel's |
None
|
Returns:
| Type | Description |
|---|---|
str
|
HTML wrapper containing a lazy-loading iframe. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
visual_tools._as_pandas(df)
Return a pandas copy from either a pandas or Spark DataFrame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
Pandas or Spark DataFrame. |
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
Independent copy, so callers can mutate it without affecting the input. |
Notes
Spark input is collected to the driver, so this is only safe for aggregates.
visual_tools._build_error_rate_by_vendor_quarter_html(logs_pdf, all_responses_df)
Build an HTML table of failed-check rates by vendor and quarter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logs_pdf
|
DataFrame
|
Pandas DataFrame of QA failure log rows. Must share a respondent
identifier column with |
required |
all_responses_df
|
Pandas or Spark DataFrame of all responses. Must contain |
required |
Returns:
| Type | Description |
|---|---|
str
|
HTML table of error rates per vendor and quarter, or an explanatory HTML
message when |
visual_tools._build_answer_count_by_vendor_quarter_html(all_responses_df)
Build an HTML table of response counts by vendor and quarter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
all_responses_df
|
Pandas or Spark DataFrame of all responses. Must contain |
required |
Returns:
| Type | Description |
|---|---|
str
|
HTML table of response counts per vendor and quarter, or an explanatory
HTML message when |
visual_tools.build_logs_report_html(df, country_code, good_records, dumped_records, svg_list, all_responses_df=None)
Build a standalone survey QA report from validation logs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
Pandas or Spark log DataFrame. Common fields include |
required |
country_code
|
str
|
Country displayed in the report title. |
required |
good_records
|
int
|
Number of responses retained by QA. |
required |
dumped_records
|
int
|
Number of responses rejected by QA. |
required |
svg_list
|
list
|
Ordered visualization strings for response time, age/gender, |
required |
all_responses_df
|
Optional full response DataFrame used to calculate |
None
|
Returns:
| Type | Description |
|---|---|
str
|
Complete standalone HTML report containing KPIs, distributions, error |
str
|
tables, visualizations, and a sample of failed rows. |
Notes
Spark inputs are collected to pandas. Text values included in tables are HTML-escaped and truncated to 500 characters.