X Tutup
#include "example_common.hpp" #include "gnuplotpp/plot.hpp" #include "gnuplotpp/presets.hpp" #include "gnuplotpp/statistics.hpp" #include #include #include #include #include #include #include #include namespace { gnuplotpp::FigureSpec make_spec(const std::string& title) { using namespace gnuplotpp; FigureSpec fs; fs.preset = Preset::Custom; apply_preset_defaults(fs); apply_style_profile(fs, StyleProfile::Tufte_Minimal); fs.size = FigureSizeInches{.w = 5.4, .h = 3.8}; fs.title = title; fs.text_mode = TextMode::Enhanced; fs.formats = {OutputFormat::Pdf, OutputFormat::Png, OutputFormat::Svg}; fs.font_fallbacks = {"Arial", "Nimbus Sans", "DejaVu Sans", "Helvetica"}; fs.panel_labels = false; fs.caption.clear(); fs.auto_layout = true; return fs; } } // namespace int main(int argc, char** argv) { using namespace gnuplotpp; const std::filesystem::path out_root = example_common::parse_out_dir(argc, argv, "out/stats_plot_examples"); std::filesystem::create_directories(out_root); std::mt19937_64 rng(1234ULL); std::normal_distribution nrm(0.2, 0.85); std::vector samples(1000); for (double& s : samples) { s = nrm(rng); } // 1) QQ plot { auto fs = make_spec("Q-Q Plot vs Normal"); Figure fig(fs); AxesSpec ax; ax.title = "Normal Q-Q Plot"; ax.xlabel = "Theoretical Quantile"; ax.ylabel = "Sample Quantile"; ax.grid = true; ax.legend = true; ax.legend_spec.position = LegendPosition::TopLeft; ax.legend_spec.boxed = true; ax.legend_spec.opaque = true; ax.legend_spec.has_font_pt = true; ax.legend_spec.font_pt = 13.0; ax.has_xtick_step = true; ax.xtick_step = 1.0; ax.has_ytick_step = true; ax.ytick_step = 1.0; fig.axes(0).set(ax); std::vector q_theory, q_sample; qq_plot_normal(samples, q_theory, q_sample); fig.axes(0).add_series(SeriesSpec{.type = SeriesType::Scatter, .label = "Samples", .has_color = true, .color = "#1f77b4"}, q_theory, q_sample); const auto [x_min_it, x_max_it] = std::minmax_element(q_theory.begin(), q_theory.end()); const auto [y_min_it, y_max_it] = std::minmax_element(q_sample.begin(), q_sample.end()); const double min_axis = std::min(*x_min_it, *y_min_it); const double max_axis = std::max(*x_max_it, *y_max_it); const double pad = 0.12 * (max_axis - min_axis); ax.has_xlim = true; ax.xmin = min_axis - pad; ax.xmax = max_axis + pad; ax.has_ylim = true; ax.ymin = min_axis - pad; ax.ymax = max_axis + pad; fig.axes(0).set(ax); std::vector x_line{min_axis - pad, max_axis + pad}; std::vector y_line{min_axis - pad, max_axis + pad}; fig.axes(0).add_series( SeriesSpec{.label = "Reference (y = x)", .has_line_width = true, .line_width_pt = 2.4, .has_color = true, .color = "#d95f02"}, x_line, y_line); if (example_common::render_figure(fig, out_root / "qq_plot" / "figures") != 0) return 1; } // 2) Violin profile { auto fs = make_spec("Violin Profile"); Figure fig(fs); AxesSpec ax; ax.title = "Violin Density Profile"; ax.xlabel = "Relative Density Width"; ax.ylabel = "Value"; ax.grid = true; ax.legend = false; ax.has_xlim = true; ax.xmin = -1.05; ax.xmax = 1.05; ax.has_ylim = true; ax.ymin = -2.6; ax.ymax = 3.2; ax.has_xtick_step = true; ax.xtick_step = 0.25; fig.axes(0).set(ax); std::vector y_grid, half_w; violin_profile(samples, y_grid, half_w, 180); const auto summary = box_summary(samples); std::vector x_l(half_w.size()), x_r(half_w.size()); for (std::size_t i = 0; i < half_w.size(); ++i) { x_l[i] = -half_w[i]; x_r[i] = half_w[i]; } std::ostringstream fill_poly; fill_poly << std::fixed << std::setprecision(6); fill_poly << "set object 20 polygon from "; for (std::size_t i = 0; i < y_grid.size(); ++i) { fill_poly << x_l[i] << "," << y_grid[i] << " to "; } for (std::size_t i = y_grid.size(); i > 0; --i) { const std::size_t j = i - 1; fill_poly << x_r[j] << "," << y_grid[j]; if (j > 0) { fill_poly << " to "; } } fill_poly << " fs transparent solid 0.32 fc rgb '#6aaed6' front"; ax.gnuplot_commands = { fill_poly.str(), "set arrow 20 from -0.30," + std::to_string(summary.q1) + " to 0.30," + std::to_string(summary.q1) + " nohead lw 1.5 lc rgb '#1f1f1f' dt 2 front", "set arrow 21 from -0.36," + std::to_string(summary.median) + " to 0.36," + std::to_string(summary.median) + " nohead lw 2.0 lc rgb '#1f1f1f' front", "set arrow 22 from -0.30," + std::to_string(summary.q3) + " to 0.30," + std::to_string(summary.q3) + " nohead lw 1.5 lc rgb '#1f1f1f' dt 2 front"}; fig.axes(0).set(ax); fig.axes(0).add_series(SeriesSpec{.label = "left", .has_line_width = true, .line_width_pt = 2.3, .has_color = true, .color = "#2c7fb8"}, x_l, y_grid); fig.axes(0).add_series(SeriesSpec{.label = "right", .has_line_width = true, .line_width_pt = 2.3, .has_color = true, .color = "#2c7fb8"}, x_r, y_grid); std::vector x_obs; std::vector y_obs; x_obs.reserve(350); y_obs.reserve(350); std::uniform_real_distribution x_jitter(-0.05, 0.05); for (int i = 0; i < 350; ++i) { x_obs.push_back(x_jitter(rng)); y_obs.push_back(samples[static_cast(i)]); } fig.axes(0).add_series( SeriesSpec{.type = SeriesType::Scatter, .label = "", .has_color = true, .color = "#2f2f2f", .has_opacity = true, .opacity = 0.45}, x_obs, y_obs); if (example_common::render_figure(fig, out_root / "violin_profile" / "figures") != 0) return 1; } // 3) Box summary + sample points { auto fs = make_spec("Box Summary"); Figure fig(fs); AxesSpec ax; ax.title = "Box Summary (Tukey)"; ax.xlabel = "Group"; ax.ylabel = "Value"; ax.grid = true; ax.legend = false; ax.has_xlim = true; ax.xmin = 0.7; ax.xmax = 1.3; ax.has_xtick_step = true; ax.xtick_step = 0.1; fig.axes(0).set(ax); const auto box = box_summary(samples); const auto [smin_it, smax_it] = std::minmax_element(samples.begin(), samples.end()); const double ypad = 0.10 * (*smax_it - *smin_it); ax.has_ylim = true; ax.ymin = *smin_it - ypad; ax.ymax = *smax_it + ypad; ax.gnuplot_commands = {"set xtics ('Sample A' 1.0) font 'Helvetica,13'"}; fig.axes(0).set(ax); std::vector x_pts; std::vector y_pts; x_pts.reserve(300); y_pts.reserve(300); std::uniform_real_distribution jitter(-0.045, 0.045); for (int i = 0; i < 300; ++i) { x_pts.push_back(1.0 + jitter(rng)); y_pts.push_back(samples[static_cast(i)]); } fig.axes(0).add_series( SeriesSpec{.type = SeriesType::Scatter, .label = "", .has_color = true, .color = "#4d4d4d", .has_opacity = true, .opacity = 0.55}, x_pts, y_pts); ax.gnuplot_commands = { "set xtics ('Sample A' 1.0) font 'Helvetica,13'", "set object 1 rect from 0.88," + std::to_string(box.q1) + " to 1.12," + std::to_string(box.q3) + " fc rgb '#4c78a8' fs solid 0.30 border lc rgb '#2f2f2f'", "set arrow 1 from 0.88," + std::to_string(box.median) + " to 1.12," + std::to_string(box.median) + " nohead lw 2.0 lc rgb '#1f1f1f'", "set arrow 2 from 1.0," + std::to_string(box.whisker_low) + " to 1.0," + std::to_string(box.q1) + " nohead lw 1.5 lc rgb '#1f1f1f'", "set arrow 3 from 1.0," + std::to_string(box.q3) + " to 1.0," + std::to_string(box.whisker_high) + " nohead lw 1.5 lc rgb '#1f1f1f'", "set arrow 4 from 0.95," + std::to_string(box.whisker_low) + " to 1.05," + std::to_string(box.whisker_low) + " nohead lw 1.5 lc rgb '#1f1f1f'", "set arrow 5 from 0.95," + std::to_string(box.whisker_high) + " to 1.05," + std::to_string(box.whisker_high) + " nohead lw 1.5 lc rgb '#1f1f1f'"}; fig.axes(0).set(ax); if (example_common::render_figure(fig, out_root / "box_summary" / "figures") != 0) return 1; } // 4) Confidence ellipse { auto fs = make_spec("Confidence Ellipse"); Figure fig(fs); AxesSpec ax; ax.title = "1/2/3-Sigma Confidence Ellipses"; ax.xlabel = "x"; ax.ylabel = "y"; ax.grid = true; ax.legend = true; ax.legend_spec.position = LegendPosition::TopLeft; ax.legend_spec.boxed = true; ax.legend_spec.opaque = true; ax.legend_spec.has_font_pt = true; ax.legend_spec.font_pt = 13.0; fig.axes(0).set(ax); std::normal_distribution nx(0.0, 1.0); std::normal_distribution ny(0.0, 0.5); std::vector x(900), y(900); for (std::size_t i = 0; i < x.size(); ++i) { const double vx = nx(rng); const double vy = ny(rng); x[i] = vx; y[i] = 0.55 * vx + vy; } fig.axes(0).add_series(SeriesSpec{.type = SeriesType::Scatter, .label = "Samples", .has_color = true, .color = "#4d4d4d", .has_opacity = true, .opacity = 0.50}, x, y); std::vector ex1, ey1; std::vector ex2, ey2; std::vector ex3, ey3; confidence_ellipse(x, y, 1.0, ex1, ey1, 240); confidence_ellipse(x, y, 2.0, ex2, ey2, 240); confidence_ellipse(x, y, 3.0, ex3, ey3, 240); fig.axes(0).add_series( SeriesSpec{.label = "1{/Symbol s} ellipse", .has_line_width = true, .line_width_pt = 2.0, .has_color = true, .color = "#1f77b4"}, ex1, ey1); fig.axes(0).add_series( SeriesSpec{.label = "2{/Symbol s} ellipse", .has_line_width = true, .line_width_pt = 2.8, .has_color = true, .color = "#e45756"}, ex2, ey2); fig.axes(0).add_series( SeriesSpec{.label = "3{/Symbol s} ellipse", .has_line_width = true, .line_width_pt = 2.0, .has_color = true, .color = "#2ca02c"}, ex3, ey3); if (example_common::render_figure(fig, out_root / "confidence_ellipse" / "figures") != 0) return 1; } // 5) Autocorrelation { auto fs = make_spec("Autocorrelation"); Figure fig(fs); AxesSpec ax; ax.title = "Autocorrelation (Lags 0..60)"; ax.xlabel = "Lag"; ax.ylabel = "\\rho(k)"; ax.grid = true; ax.legend = true; ax.legend_spec.position = LegendPosition::TopRight; ax.legend_spec.boxed = true; ax.legend_spec.opaque = true; ax.legend_spec.has_font_pt = true; ax.legend_spec.font_pt = 13.0; ax.has_xlim = true; ax.xmin = -0.5; ax.xmax = 60.5; ax.has_ylim = true; ax.ymin = -0.2; ax.ymax = 1.05; ax.has_xtick_step = true; ax.xtick_step = 10.0; ax.has_ytick_step = true; ax.ytick_step = 0.2; fig.axes(0).set(ax); std::vector sig(800); sig[0] = 0.0; std::normal_distribution wn(0.0, 1.0); for (std::size_t i = 1; i < sig.size(); ++i) { sig[i] = 0.86 * sig[i - 1] + 0.25 * wn(rng); } const auto ac = autocorrelation(sig, 60); std::vector lags(ac.size()); for (std::size_t i = 0; i < lags.size(); ++i) lags[i] = static_cast(i); const double ci = 1.96 / std::sqrt(static_cast(sig.size())); std::vector ci_pos(ac.size(), ci); std::vector ci_neg(ac.size(), -ci); fig.axes(0).add_histogram( SeriesSpec{.label = "ACF", .has_color = true, .color = "#4c78a8", .has_opacity = true, .opacity = 0.42, .has_line_width = true, .line_width_pt = 1.4}, lags, ac); fig.axes(0).add_series(SeriesSpec{.label = "95% bounds", .has_line_width = true, .line_width_pt = 2.0, .has_color = true, .color = "#d95f02"}, lags, ci_pos); fig.axes(0).add_series(SeriesSpec{.label = "", .has_line_width = true, .line_width_pt = 2.0, .has_color = true, .color = "#d95f02"}, lags, ci_neg); if (example_common::render_figure(fig, out_root / "autocorrelation" / "figures") != 0) return 1; } gnuplotpp::log::Info("generated stats examples under: ", out_root.string()); return 0; }
X Tutup