Skip to content

10 · Project — Data Analysis & Plotting Script

Time to combine everything from this level into one working script: reading data, computing statistics, filtering with logical indexing, writing a function, and producing a labeled plot.

Verification note

MATLAB was not available in this environment. Every number below was hand-computed (mean, sample standard deviation with n-1 denominator, sorted order, threshold filter) and cross-checked with an independent Python calculation using the same formulas MATLAB's mean/std functions document — not executed in MATLAB itself.

The scenario

You have exam scores for 10 students and want a script that: loads the data, reports summary statistics, flags who passed (score ≥ 70), and produces a plot showing the distribution with the passing threshold marked.

Step 1 — the data

scores = [72, 88, 95, 61, 79, 84, 90, 55, 68, 99];
students = {'Ana', 'Ben', 'Cara', 'Dev', 'Ella', ...
            'Finn', 'Gia', 'Hao', 'Ivy', 'Jai'};

(This mirrors what readtable would give you from a real CSV — the project works the same way whether the data is typed in directly or loaded from a file, which is exactly the point of Module 07.)

Step 2 — a reusable statistics function

function report = analyze_scores(scores)
    report.mean_score = mean(scores);
    report.std_score  = std(scores);
    report.max_score  = max(scores);
    report.min_score  = min(scores);
    report.sorted     = sort(scores);
end
>> r = analyze_scores(scores)
r =

  struct with fields:

    mean_score: 79.1000
     std_score: 14.7305
     max_score: 99
     min_score: 55
        sorted: [55 61 68 72 79 84 88 90 95 99]

std() uses n-1 in the denominator by default (the sample standard deviation, Bessel's correction) — the right choice when your data is a sample rather than the entire population, and MATLAB's documented default.

Step 3 — filtering with logical indexing

passing_mask = scores >= 70;
passing_scores = scores(passing_mask);
passing_names = students(passing_mask);   % cell arrays support logical indexing too

fprintf('%d of %d students passed (%.0f%%)\n', ...
    sum(passing_mask), length(scores), 100 * mean(passing_mask));
7 of 10 students passed (70%)

mean(passing_mask) works because passing_mask is a logical array — MATLAB treats true/false as 1/0 in arithmetic, so the mean of a logical array is exactly the fraction that's true, a common one-line idiom for "what percentage passed."

>> passing_scores
passing_scores =

    72    88    95    79    84    90    99

>> passing_names
passing_names =

  1x7 cell array

    {'Ana'}    {'Ben'}    {'Cara'}    {'Ella'}    {'Finn'}    {'Gia'}    {'Jai'}

Step 4 — the plot

figure
bar(scores)
hold on
yline(70, 'r--', 'Passing Threshold', 'LineWidth', 2);
set(gca, 'XTickLabel', students, 'XTick', 1:length(students))
xlabel('Student')
ylabel('Score')
title(sprintf('Exam Scores (Mean: %.1f, Pass rate: %.0f%%)', ...
    mean(scores), 100 * mean(passing_mask)))
grid on
saveas(gcf, 'exam_scores_report.png')

yline(70, ...) draws a horizontal reference line at y = 70 across the whole plot, with an inline label — a clean way to show a threshold against bar or line data without manually plotting a second series.

Step 5 — the complete script

% exam_analysis.m
scores = [72, 88, 95, 61, 79, 84, 90, 55, 68, 99];
students = {'Ana', 'Ben', 'Cara', 'Dev', 'Ella', ...
            'Finn', 'Gia', 'Hao', 'Ivy', 'Jai'};

report = analyze_scores(scores);
fprintf('Mean: %.2f | Std: %.2f | Max: %d | Min: %d\n', ...
    report.mean_score, report.std_score, report.max_score, report.min_score);

passing_mask = scores >= 70;
fprintf('%d of %d students passed (%.0f%%)\n', ...
    sum(passing_mask), length(scores), 100 * mean(passing_mask));

figure
bar(scores)
hold on
yline(70, 'r--', 'Passing Threshold', 'LineWidth', 2);
set(gca, 'XTickLabel', students, 'XTick', 1:length(students))
xlabel('Student'); ylabel('Score')
title(sprintf('Exam Scores (Mean: %.1f, Pass rate: %.0f%%)', ...
    mean(scores), 100 * mean(passing_mask)))
grid on
saveas(gcf, 'exam_scores_report.png')

function report = analyze_scores(scores)
    report.mean_score = mean(scores);
    report.std_score  = std(scores);
    report.max_score  = max(scores);
    report.min_score  = min(scores);
    report.sorted     = sort(scores);
end

Running exam_analysis in the Command Window prints the summary lines, opens a labeled bar chart with a threshold line, and saves it to a PNG — one script covering statistics, logical filtering, functions, and plotting, the full arc of this level.

What this project used from every module

Module Used here as
01 · What Is MATLAB? Script structure, running a .m file
02 · Variables & Types scores (double array), students (cell of char)
03 · Vectors & Matrix Ops scores >= 70, mean(), sort()
04 · Control Flow (implicit — logical indexing replaces an explicit loop)
05 · Functions analyze_scores, returning a struct
06 · Plotting bar, yline, labels, saveas
07 · Data Files Same shape as data from readtable
08 · String Processing sprintf/fprintf for the report messages
09 · Numerical Methods mean/std as summary statistics

How It Actually Works

A typical "load, clean, summarize, plot" analysis script exercises nearly every MATLAB execution mechanism at once, and understanding the pipeline end to end explains its performance characteristics. readtable/text import pays a parsing cost proportional to file size (each numeric field goes through a string-to-double conversion); once the data lives in a table or numeric array, it's a contiguous column-major buffer in memory, so column-wise operations (mean(data(:,2))) are cache-friendly while row-wise scans are not. Logical indexing for filtering (data(data(:,1) > threshold, :)) is itself a two-pass operation internally: MATLAB first builds a full logical mask array the same size as the column being tested (one comparison per element), then uses that mask to gather matching rows into a freshly allocated output array — it is not free, but it is still far cheaper than an equivalent explicit loop because both passes run as tight vectorized (and often JIT- or BLAS-backed) operations rather than per-element interpreter dispatch.

Aggregate functions like mean, std, and sum call into MATLAB's underlying numerical libraries (ultimately BLAS/LAPACK-style routines for anything matrix-shaped), which is why they scale far better than a hand-written accumulation loop over the same data — the loop pays per-iteration interpreter overhead for every element, while the built-in reduces the whole operation to a small number of calls into compiled, cache-tuned native code.

Note: reasoned from MATLAB's documented data-import, indexing, and built-in-function execution model; not executed in a live MATLAB session.

🔀 See this in another language

Exercise

Extend exam_analysis.m: add a second local function, grade_letter(score), that returns 'A' for ≥ 90, 'B' for ≥ 80, 'C' for ≥ 70, and 'F' otherwise (using if/elseif from Module 04). Loop over scores with a for loop, building a cell array grades of each student's letter grade, and add a column of grade labels above each bar using text() at each bar's x/y position. Save the final annotated figure as exam_scores_with_grades.png.