|
| 1 | +""" pyplots.ai |
| 2 | +bar-permutation-importance: Permutation Feature Importance Plot |
| 3 | +Library: pygal 3.1.0 | Python 3.13.11 |
| 4 | +Quality: 83/100 | Created: 2025-12-31 |
| 5 | +""" |
| 6 | + |
| 7 | +import numpy as np |
| 8 | +import pygal |
| 9 | +from pygal.style import Style |
| 10 | + |
| 11 | + |
| 12 | +# Data - Simulated permutation importance results (realistic ML feature importance) |
| 13 | +np.random.seed(42) |
| 14 | + |
| 15 | +# Feature names representing typical ML model features (sorted by importance, ascending) |
| 16 | +features = [ |
| 17 | + "Year Built", |
| 18 | + "Bathrooms", |
| 19 | + "Garage Size", |
| 20 | + "Lot Area", |
| 21 | + "Bedrooms", |
| 22 | + "Basement Area", |
| 23 | + "Total Rooms", |
| 24 | + "Living Area", |
| 25 | + "Neighborhood", |
| 26 | + "Overall Quality", |
| 27 | +] |
| 28 | + |
| 29 | +# Mean importance values (mean decrease in R² score from permutation) |
| 30 | +# Sorted ascending for bottom-to-top display in horizontal bar |
| 31 | +importance_mean = np.array([0.002, 0.008, 0.015, 0.024, 0.032, 0.048, 0.067, 0.095, 0.128, 0.185]) |
| 32 | + |
| 33 | +# Standard deviation across 10 permutation repetitions |
| 34 | +importance_std = np.array([0.003, 0.005, 0.008, 0.011, 0.014, 0.018, 0.022, 0.028, 0.035, 0.042]) |
| 35 | + |
| 36 | +# Generate sequential color gradient (viridis-inspired) inline |
| 37 | +min_imp = importance_mean.min() |
| 38 | +max_imp = importance_mean.max() |
| 39 | +imp_range = max_imp - min_imp if max_imp != min_imp else 1.0 |
| 40 | + |
| 41 | +# Viridis color stops: purple (0) -> teal (0.5) -> yellow (1) |
| 42 | +viridis_stops = [(0.0, 68, 1, 84), (0.25, 58, 82, 139), (0.5, 32, 144, 140), (0.75, 94, 201, 97), (1.0, 253, 231, 36)] |
| 43 | + |
| 44 | + |
| 45 | +def get_viridis_color(t): |
| 46 | + """Interpolate viridis color for normalized value t in [0, 1].""" |
| 47 | + for j in range(len(viridis_stops) - 1): |
| 48 | + t0, r0, g0, b0 = viridis_stops[j] |
| 49 | + t1, r1, g1, b1 = viridis_stops[j + 1] |
| 50 | + if t0 <= t <= t1: |
| 51 | + seg_t = (t - t0) / (t1 - t0) |
| 52 | + r = int(r0 + (r1 - r0) * seg_t) |
| 53 | + g = int(g0 + (g1 - g0) * seg_t) |
| 54 | + b = int(b0 + (b1 - b0) * seg_t) |
| 55 | + return f"#{r:02x}{g:02x}{b:02x}" |
| 56 | + return "#fde724" |
| 57 | + |
| 58 | + |
| 59 | +bar_colors = [get_viridis_color((imp - min_imp) / imp_range) for imp in importance_mean] |
| 60 | + |
| 61 | +# Custom style with larger fonts for 4800x2700 canvas and subtle guides |
| 62 | +custom_style = Style( |
| 63 | + background="white", |
| 64 | + plot_background="white", |
| 65 | + foreground="#333333", |
| 66 | + foreground_strong="#333333", |
| 67 | + foreground_subtle="#999999", |
| 68 | + guide_stroke_color="#cccccc", |
| 69 | + major_guide_stroke_color="#cccccc", |
| 70 | + colors=tuple(bar_colors), |
| 71 | + title_font_size=84, |
| 72 | + label_font_size=48, |
| 73 | + major_label_font_size=48, |
| 74 | + legend_font_size=44, |
| 75 | + value_font_size=40, |
| 76 | + stroke_width=2, |
| 77 | + font_family="sans-serif", |
| 78 | + opacity=0.95, |
| 79 | + guide_stroke_dasharray="4,4", |
| 80 | +) |
| 81 | + |
| 82 | +# Create horizontal stacked bar chart for error bar visualization |
| 83 | +# First stack: mean value (solid), Second stack: +std (semi-transparent for error bar effect) |
| 84 | +chart = pygal.HorizontalStackedBar( |
| 85 | + width=4800, |
| 86 | + height=2700, |
| 87 | + style=custom_style, |
| 88 | + title="bar-permutation-importance · pygal · pyplots.ai", |
| 89 | + x_title="Mean Decrease in R² Score", |
| 90 | + show_legend=True, |
| 91 | + legend_at_bottom=True, |
| 92 | + legend_at_bottom_columns=2, |
| 93 | + legend_box_size=36, |
| 94 | + print_values=True, |
| 95 | + print_values_position="top", |
| 96 | + show_y_guides=False, |
| 97 | + show_x_guides=True, |
| 98 | + truncate_label=-1, |
| 99 | + spacing=28, |
| 100 | + margin=80, |
| 101 | + margin_left=420, |
| 102 | + margin_bottom=200, |
| 103 | + range=(-0.02, importance_mean.max() + importance_std.max() + 0.02), |
| 104 | + zero=0, |
| 105 | +) |
| 106 | + |
| 107 | +# Set feature labels on y-axis |
| 108 | +chart.x_labels = features |
| 109 | + |
| 110 | +# Create data series: main bars (mean values) and error extensions (+std) |
| 111 | +mean_data = list(importance_mean) |
| 112 | +std_data = list(importance_std) |
| 113 | + |
| 114 | +# Add mean importance bars with viridis colors |
| 115 | +chart.add( |
| 116 | + "Mean Importance", |
| 117 | + [{"value": v, "color": c} for v, c in zip(mean_data, bar_colors, strict=True)], |
| 118 | + formatter=lambda x: f"{x:.3f}" if x else "", |
| 119 | +) |
| 120 | + |
| 121 | +# Add error bar extensions (std values) with semi-transparent styling |
| 122 | +error_color = "rgba(100, 100, 100, 0.35)" |
| 123 | +chart.add( |
| 124 | + "± Std Dev (Error Range)", |
| 125 | + [{"value": v, "color": error_color} for v in std_data], |
| 126 | + formatter=lambda x: f"±{x:.3f}" if x else "", |
| 127 | +) |
| 128 | + |
| 129 | +# Save outputs |
| 130 | +chart.render_to_file("plot.html") |
| 131 | +chart.render_to_png("plot.png") |
0 commit comments