import base64 import io import math import threading from functools import lru_cache import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import numpy as np from matplotlib.ticker import FuncFormatter _FIGURE_LOCK = threading.RLock() def _png_data_uri(fig): buffer = io.BytesIO() try: fig.savefig(buffer, format='png', dpi=130, bbox_inches='tight') encoded = base64.b64encode(buffer.getvalue()).decode('ascii') return f'data:image/png;base64,{encoded}' finally: buffer.close() plt.close(fig) def _format_number(value): value = float(value) if value.is_integer(): return str(int(value)) return f'{value:g}' def _percent_formatter(): return FuncFormatter(lambda value, _position: f'{value:.0f}%') def _clean_step(target): if target <= 0: return 1.0 magnitude = 10 ** np.floor(np.log10(target)) scaled = target / magnitude for multiplier in (1, 2, 2.5, 4, 5, 6, 8, 10): if scaled <= multiplier: return float(multiplier * magnitude) return float(10 * magnitude) def _symmetric_bins(center, radius, bin_width): steps = int(np.ceil(radius / bin_width)) clean_radius = steps * bin_width return np.arange( center - clean_radius, center + clean_radius + bin_width, bin_width, ) def _centered_symmetric_bins(center, radius, bin_width): steps = int(np.ceil(radius / bin_width)) edge_radius = (steps + 0.5) * bin_width return np.arange( center - edge_radius, center + edge_radius + bin_width, bin_width, ) def _normal_cdf(x, mean, sigma): z = (x - mean) / (sigma * math.sqrt(2)) return 0.5 * (1 + math.erf(z)) def _normal_bin_percentages(bins, mean, sigma): return np.array([ 100.0 * ( _normal_cdf(bins[i + 1], mean, sigma) - _normal_cdf(bins[i], mean, sigma) ) for i in range(len(bins) - 1) ]) def _plot_normal_bars(ax, bins, percentages, color): bin_centers = (bins[:-1] + bins[1:]) / 2 bin_widths = np.diff(bins) ax.bar( bin_centers, percentages, width=bin_widths, align='center', color=color, edgecolor='white', linewidth=1.2, ) def _percent_y_limit(max_percent): return max(22.0, float(np.ceil((max_percent * 1.12) / 2.0) * 2.0)) def _build_true_quality_figure(mean, standard_deviation): bin_width = _clean_step(standard_deviation / 2.0) bins = _centered_symmetric_bins(mean, 3.0 * standard_deviation, bin_width) percentages = _normal_bin_percentages( bins, mean, standard_deviation, ) x_min = bins[0] x_max = bins[-1] y_max = _percent_y_limit(float(percentages.max())) fig, ax = plt.subplots(figsize=(7.6, 4.6)) _plot_normal_bars(ax, bins, percentages, '#8d8f8f') ax.axvline(mean, color='#c9cdd2', linestyle='--', linewidth=1.3) ax.set_title( 'Distribution of True Quality\n' f'(Normally distributed with Mean {_format_number(mean)}, ' f'Standard Deviation {_format_number(standard_deviation)})', fontsize=15, ) ax.set_xlabel('True Quality', fontsize=13) ax.set_ylabel('Percent of Candidates', fontsize=13) ax.set_xlim(x_min, x_max) ax.set_ylim(0, y_max) tick_step = _clean_step((x_max - x_min) / 5.0) tick_start = np.ceil(x_min / tick_step) * tick_step tick_end = np.floor(x_max / tick_step) * tick_step ax.set_xticks(np.arange(tick_start, tick_end + tick_step, tick_step)) ax.yaxis.set_major_formatter(_percent_formatter()) ax.grid(False) ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False) arrow_y = -0.24 ax.annotate( '', xy=(0.18, arrow_y), xytext=(0.42, arrow_y), xycoords='axes fraction', arrowprops=dict(arrowstyle='->', linewidth=1.8, color='black'), annotation_clip=False, ) ax.text( 0.18, arrow_y - 0.08, 'Low Quality', transform=ax.transAxes, ha='center', va='top', fontsize=12, ) ax.annotate( '', xy=(0.82, arrow_y), xytext=(0.58, arrow_y), xycoords='axes fraction', arrowprops=dict(arrowstyle='->', linewidth=1.8, color='black'), annotation_clip=False, ) ax.text( 0.82, arrow_y - 0.08, 'High Quality', transform=ax.transAxes, ha='center', va='top', fontsize=12, ) fig.subplots_adjust(bottom=0.27, top=0.82, left=0.12, right=0.98) return _png_data_uri(fig) def _build_error_distribution_panel(ax, bins, stage_name, sigma, color): percentages = _normal_bin_percentages(bins, 0, sigma) _plot_normal_bars(ax, bins, percentages, color) ax.axvline(0, color='#d0d7de', linestyle='--', linewidth=1.2) ax.set_title( f'Distribution of {stage_name} Evaluation Errors\n' f'(Normally distributed with Mean 0, Standard Deviation {_format_number(sigma)})', fontsize=13, ) ax.set_ylabel('Percent of Candidates', fontsize=11) ax.yaxis.set_major_formatter(_percent_formatter()) ax.grid(False) ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False) def _build_noise_figure(sigma_resume, sigma_zoom, sigma_inperson): parameters = [ ('Resume', sigma_resume, '#5B9BD5'), ('Zoom Interview', sigma_zoom, '#F4A352'), ('In-person Interview', sigma_inperson, '#82B366'), ] maximum_sigma = max(sigma_resume, sigma_zoom, sigma_inperson) x_limit_step = 10.0 x_limit = np.ceil((2.5 * maximum_sigma) / x_limit_step) * x_limit_step bin_width = _clean_step(maximum_sigma / 4.0) bins = _centered_symmetric_bins(0.0, x_limit, bin_width) x_limit = max(abs(bins[0]), abs(bins[-1])) y_max = _percent_y_limit( max( float(_normal_bin_percentages(bins, 0, sigma).max()) for _stage_name, sigma, _color in parameters ) ) fig, axes = plt.subplots(3, 1, figsize=(7.6, 8.4), sharex=True) for ax, (stage_name, sigma, color) in zip( axes, parameters, ): _build_error_distribution_panel( ax, bins, stage_name, sigma, color, ) ax.set_xlim(-x_limit, x_limit) ax.set_ylim(0, y_max) axes[-1].set_xlabel('Evaluation Error', fontsize=14) arrow_y = -0.34 axes[-1].annotate( '', xy=(0.18, arrow_y), xytext=(0.42, arrow_y), xycoords='axes fraction', arrowprops=dict(arrowstyle='->', linewidth=1.8, color='black'), annotation_clip=False, ) axes[-1].text( 0.18, arrow_y - 0.08, 'Evaluation score\nunderestimates true quality', transform=axes[-1].transAxes, ha='center', va='top', fontsize=10.5, ) axes[-1].annotate( '', xy=(0.82, arrow_y), xytext=(0.58, arrow_y), xycoords='axes fraction', arrowprops=dict(arrowstyle='->', linewidth=1.8, color='black'), annotation_clip=False, ) axes[-1].text( 0.82, arrow_y - 0.08, 'Evaluation score\noverestimates true quality', transform=axes[-1].transAxes, ha='center', va='top', fontsize=10.5, ) fig.subplots_adjust(hspace=0.38, bottom=0.17, top=0.94, left=0.13, right=0.98) return _png_data_uri(fig) @lru_cache(maxsize=32) def _generate_evaluation_distribution_figures_cached( true_quality_mean, true_quality_standard_deviation, sigma_resume, sigma_zoom, sigma_inperson, ): with _FIGURE_LOCK: true_quality_uri = _build_true_quality_figure( true_quality_mean, true_quality_standard_deviation, ) noise_uri = _build_noise_figure( sigma_resume, sigma_zoom, sigma_inperson, ) return { 'true_quality_distribution_uri': true_quality_uri, 'noise_distribution_uri': noise_uri, 'figure_parameters': { 'true_quality_mean': true_quality_mean, 'true_quality_standard_deviation': true_quality_standard_deviation, 'sigma_resume': sigma_resume, 'sigma_zoom': sigma_zoom, 'sigma_inperson': sigma_inperson, }, 'figure_generation_error': '', } def generate_evaluation_distribution_figures( true_quality_mean, true_quality_standard_deviation, sigma_resume, sigma_zoom, sigma_inperson, ): parameter_tuple = tuple( float(value) for value in ( true_quality_mean, true_quality_standard_deviation, sigma_resume, sigma_zoom, sigma_inperson, ) ) return _generate_evaluation_distribution_figures_cached(*parameter_tuple)