<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://huong-vo.github.io/feed.xml" rel="self" type="application/atom+xml"/><link href="https://huong-vo.github.io/" rel="alternate" type="text/html" hreflang="en"/><updated>2025-08-21T04:46:26+00:00</updated><id>https://huong-vo.github.io/feed.xml</id><title type="html">blank</title><subtitle>A simple, whitespace theme for academics. Based on [*folio](https://github.com/bogoli/-folio) design. </subtitle><entry><title type="html">Machine Learning 102</title><link href="https://huong-vo.github.io/blog/2025/KNN102/" rel="alternate" type="text/html" title="Machine Learning 102"/><published>2025-08-16T00:00:00+00:00</published><updated>2025-08-16T00:00:00+00:00</updated><id>https://huong-vo.github.io/blog/2025/KNN102</id><content type="html" xml:base="https://huong-vo.github.io/blog/2025/KNN102/"><![CDATA[<p><strong>Acknowledgement:</strong> This post is based mainly on Dr. Vinh-Dinh Nguyen’s presentation and slides and partly on on TA, STA, and peers’ responses from the course AIO2025 offered by AI VIETNAM.</p> <p><strong>Relevance:</strong> The audience of this lecture were those who have read <a href="/blog/2025/KMeans101/">Machine Learning 101 Warm-up</a>, had some coding experience, and known a bit of AI knowledge. 2025-08-05-KNN-pre101</p> <p>In today’s post, we’ll explore KNN and Supervised Learning. We’ll also discuss some applications in AI including a text classification problem and a medical case study.</p> <hr/> <h2 id="machine-learning"><strong>Machine Learning</strong></h2> <p><strong>Machine Learning (ML)</strong> is a subfield of <em>Artificial Intelligence (AI)</em>, referring to learning the best approximation of a function from the training data so that data can be predicted accurately.</p> <pre><code class="language-mermaid">mindmap
    root)Machine Learning(
        ((&lt;strong&gt;Semi-Supervised&lt;br/&gt; Learning&lt;/strong&gt;))
        ((&lt;strong&gt;Supervised&lt;br/&gt; Learning&lt;/strong&gt;))
            &lt;/strong&gt;Classification&lt;/strong&gt;
                (Identity Fraud Detection)
                (Image Classification)
                (Customer Retention)
                (Diagnostics)
            &lt;strong&gt;Regression&lt;/strong&gt;
                (Advertising Popularity Prediction)
                (Weather Forecasting)
                (Markekt Forecasting)
                (Estimating Life Expectancy)
                (Population Growth Prediction)
        ((&lt;strong&gt;Unsupervised&lt;br/&gt; Learning&lt;/strong&gt;))
            &lt;strong&gt;Clustering&lt;/strong&gt;
                (Recommender Systems)
                (Targetted Marketing)
                (Customer Segmentation)
            &lt;strong&gt;Dimensionality&lt;br/&gt; Reduction&lt;/strong&gt;
                (Big Data Visualization)
                (Meaningful Compression)
                (Structure Discovery)
                (Feature Elicitation)
        ((&lt;strong&gt;Reinforcement&lt;br/&gt; Learning&lt;/strong&gt;))
            (Real-time Decisions)
            (Robot Navigation)
            (Learning Tasks)
            (Skill Acquisition)
            (Game AI)
</code></pre> <figcaption style="text-align: center; width: 100%; font-size: 0.875em; margin-top:-5px; margin-bottom:15px;"> <em>Figure 1.</em> Machine Learning Overview </figcaption> <p>There are four main types of <strong>ML</strong>:</p> <ul> <li><strong>Supervised Learning (SL):</strong> learning <em>with guidance—from a labeled dataset</em> to map features to a potentially correct label. <ul> <li>Examples: predicting the shape of a geometric object after learning a labeled dataset and predicting the linear relationship between features and label.</li> </ul> </li> <li><strong>Unsupervised Learning (USL):</strong> learning <em>without explicit guidance or labeled data</em> to stamp labels for data points. <ul> <li>Examples: labeling a dataset of animal images.</li> </ul> </li> <li><strong>Semi-Supervised Learning (SSL):</strong> a combination of SL and USL.</li> <li><strong>Reinforcement Learning (RL):</strong> learning via interactions with external environments and <em>evaluation-reward-punishment process</em> to maximize the cumulative reward. This is a <em>trial-and-error approach</em> where agents, just like a human, learn from feedback and adjust its actions until getting a desired outcome. <ul> <li>Examples: autonomous vehicles or chess playing.</li> </ul> </li> </ul> <pre><code class="language-mermaid">flowchart TB
    subgraph OutputTitle[Output]
        direction LR
        mapping(Mapping):::output
        classes(Classes):::output
        actions(Actions):::output
    end

    subgraph InputTitle[Input]
        direction TB
        labeledData(Data with Labels):::input
        unlabeledData(Data without Labels):::input
        statesActions(States and Actions):::input
    end

    labeledData --&gt; SL ----&gt; mapping
    criticsSL((" ")):::cSL --&gt;|Error| SL(Supervised Learning):::SL -.-&gt;|Targets| criticsSL
    unlabeledData --&gt; USL(Unsupervised&lt;br/&gt; Learning):::USL ----&gt; classes
    statesActions --&gt; RL(Reinforcement&lt;br/&gt; Learning):::RL ----&gt; actions
    criticsRL((" ")):::cRL --&gt;|Reward| RL -.-&gt;|Evaluations| criticsRL

    classDef input fill:#fec3ec,stroke:#7293ba,stroke-width:2px,font-family:serif;
    classDef output fill:#99ffff,stroke:#628e6e,stroke-width:2px,font-family:serif;
    classDef outputHeader fill:#bff3fd,stroke:#3b6fa6,stroke-width:2.5px,font-weight:bold,text-align:center;
    classDef inputHeader fill:#f8dcfe,stroke:#cd7a39,stroke-width:2.5px,font-weight:bold,text-align:center;
    classDef SL fill:#adff00,stroke:#7293ba,stroke-width:2px,font-family:serif,font-weight:bold;
    classDef USL fill:#fffb96,stroke:#7293ba,stroke-width:2px,font-family:serif,font-weight:bold;
    classDef RL fill:#b967ff,stroke:#7293ba,stroke-width:2px;
    classDef cSL fill:#74d600,stroke:#7293ba,stroke-width:1px;
    classDef cRL fill:#962fbf,stroke:#7293ba,stroke-width:1px;
    classDef blockHeader fill:none,stroke-width:0px;

    class OutputTitle outputHeader
    class InputTitle inputHeader
    class SLblock blockHeader
    class RLblock blockHeader
</code></pre> <figcaption style="text-align: center; width: 100%; font-size: 0.875em; margin-top:-5px; margin-bottom:15px;"> <em>Figure 2.</em> Machine Learning Procedure Overview </figcaption> <h3 id="approaches"><strong>Approaches</strong></h3> <p>There are several appproaches for a ML problem.</p> <ul> <li><strong>Parametric Approach:</strong> This is a classic SL. We try to parametrize the function, so we need to go through some important steps. <ul> <li>Identify if the function is linear or non-linear.</li> <li>Identify the parameters for a linear function/regression.</li> </ul> </li> <li><strong>Eager Learning Approach:</strong> We build and train a model/function based on the entire training dataset before the prediction phase starts. <ul> <li>Spend a long time learning and building the function.</li> <li>Spend less time classifying or inferring the function.</li> </ul> </li> <li><strong>Model-based Learning Approach:</strong> We build a model that captures parttern in the data. <ul> <li>Spend a long time learning and building the function.</li> <li>Spend less time classifying or inferring the function.</li> </ul> </li> </ul> <p>Eager and model-based learning methods can be both parametric and non-parametric.</p> <hr/> <h2 id="k-nearest-neighbors-knn-overview"><strong>$K$-Nearest Neighbors (KNN) Overview</strong></h2> <p>The parametric approach <em>technically</em> involves the assumptions of having/looking for a fixed number of parameters regardless of dataset size. This means the method <em>strongly relies on</em> the assumptions about the data. which could lead to poor performance if the assumptions are incorrect. This is where KNN comes in as a <em>non-parametric approach</em>.</p> <h3 id="motivation"><strong>Motivation</strong></h3> <p><strong>KNN</strong> is an algorithm where</p> <ul> <li>there is <strong>no assumptions</strong> made about underlying data distribution.</li> <li><strong>learning takes less time</strong> and <strong>testing takes more time</strong>. <ul> <li>a training dataset is <em>stored without learning</em> a function or building a model <em>during the training phase</em>.</li> <li><em>predictions</em> are made on data <em>only when</em> KNN actually <em>receives test data</em>.</li> </ul> </li> </ul> <p>These properties are the reasons for <strong>KNN</strong> to be called <strong>lazy learning</strong> or <strong>instance-based learning</strong>. You can think of it as a lazy student partaking in an exam with no studying or revising in advance.</p> <p>A typical example is a given image dataset of different kinds of insects and animals with known categories. If we are given a covered image with its intensity matrix and need to predict which group it belongs to, KNN will look for its $K$ nearest neighbors from the dataset to choose a potentially correct category. “Nearest” refers to the closet distance.</p> <h3 id="procedure"><strong>Procedure</strong></h3> <p>:pushpin: <strong>Note:</strong> The value of $K$ can completely change your model’s prediction. That’s because KNN picks the category by majority vote among the $K$ closet neighbors. Thus, <em>choosing the optimal $K$</em> plays a critical role in KNN.</p> <p>We should always preprocess the data befor running KNN. Once ready, the algorithm follows a simple flow:</p> <ol> <li><em>Initialize</em> the value of $K$.</li> <li><em>Compute the <strong>similar feature</strong>/distance</em> between test point and all data points (every row of the training dataset).</li> <li><em>Sort</em> the calculated distances in ascending order.</li> <li><em>Select top $K$ elements</em> from the sorted array.</li> <li><em>Vote the most frequent label</em> of these elements.</li> <li><em>Predict</em>—return the predicted class.</li> </ol> <pre><code class="language-mermaid">flowchart LR
    A((&lt;strong&gt;Initialize k&lt;/strong&gt;)) ---&gt; B((&lt;strong&gt;Compute distances&lt;/strong&gt;&lt;/br&gt; between test data&lt;/br&gt; and training data)) ---&gt; C((&lt;strong&gt;Sort distances&lt;/strong&gt;&lt;/br&gt; in ascending order)) ---&gt; D((&lt;strong&gt;Get top k elements&lt;/strong&gt;&lt;/br&gt; from the sorted array)) ---&gt; E((&lt;strong&gt;Get the most frequent class&lt;/strong&gt;&lt;/br&gt; of these elements)) ---&gt; F((&lt;strong&gt;Return predicted class&lt;/strong&gt;))
</code></pre> <figcaption style="text-align: center; width: 100%; font-size: 0.875em; margin-top:-5px; margin-bottom:15px;"> <em>Figure 3.</em> KNN Procedure </figcaption> <p>In step 5, we may run into a situation where two or more labels appear equally often. This occurs especially when $K$ is even. We’ll explore strategies to handle this later.</p> <p>For step 2, there is <em>no</em> generic formula for choosing a <strong>metric</strong>. A good practice is to understand the pros and cons of each metric to decide if it is suitable for the dataset. Also, we can experiment them to see which metric gives the most consistent and meaningful results for the dataset. Common <strong>metrics</strong> to compute the similarity between features are</p> <ul> <li>cosine similarity</li> <li>correlation coefficient</li> <li>L1 (absolute value)</li> <li>L2 (Euclidean distance)</li> <li>Hamming distance</li> <li>Manhattan distance</li> <li>Minkowski distance</li> <li>Chebyshev distance</li> <li>Jaccard distance</li> <li>Haversine formula</li> <li>Sørensen–Dice coefficient.</li> </ul> <p><del>DEMONSTRATION FOR METRIC from slides maybe</del></p> <p>We can use an available package for KNN with its default metric being Minkowski distance.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># call the package with initialized K
# specify which metric via p, p=2 indicated Euclidean distance
</span><span class="n">knn</span> <span class="o">=</span> <span class="nc">KNeighborsClassifier</span><span class="p">(</span><span class="n">n_neighbors</span><span class="o">=</span><span class="n">K</span><span class="p">,</span> <span class="n">p</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>

<span class="c1"># save and arrange dataset
</span><span class="n">knn</span><span class="p">.</span><span class="nf">fit</span><span class="p">(</span><span class="n">X_train</span><span class="p">,</span> <span class="n">y_train</span><span class="p">)</span>

<span class="c1"># make predictions
</span><span class="n">pred</span> <span class="o">=</span> <span class="n">knn</span><span class="p">.</span><span class="nf">predict</span><span class="p">(</span><span class="n">X_test</span><span class="p">)</span>
</code></pre></div></div> <h2 id="selecting-k-in-knn"><strong>Selecting $K$ in KNN</strong></h2> <p>As mentioned earlier, different $K$ may give different results. Thus, it’s imperative we choose $K$ that provides the best approximated outcome. One approach is by trial and error—figuring out the value of $K$ holding the highest accuracy after the training process.</p> <p>:question: This boils down to the question, <strong>How can we evaluate a model’s performance based on the choice of $K$?</strong></p> <p>We can use the following ready-to-use package for this purpose.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">sklearn.metrics</span> <span class="kn">import</span> <span class="n">accuracy_score</span><span class="p">,</span> <span class="n">classification_report</span>

<span class="c1"># compute the accuracy percentage
</span><span class="nf">accuracy_score</span><span class="p">(</span><span class="n">y_test</span><span class="p">,</span> <span class="n">y_pred</span><span class="p">)</span>

<span class="c1"># compute the set of precision, recall, and f1-score
</span><span class="nf">classification_report</span><span class="p">(</span><span class="n">y_test</span><span class="p">,</span> <span class="n">y_pred</span><span class="p">)</span>
</code></pre></div></div> <h3 id="evaluating-a-models-performance"><strong>Evaluating a Model’s Performance</strong></h3> <p>In practice, we use the values of <strong>precision</strong>, <strong>recall</strong>, and <strong>F1-score</strong> to assess a model’s performance. This is because the (naive) accuracy is <em>not</em> a reliable metric to evaluate a model’s performance. A very high accuracy may lead to a behavior called <strong>overfitting</strong>. See the analysis below.</p> <details class="bordered-block"> <summary> <strong>Error Analysis</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Problem statement:</strong> Cancer classification of $10000$ patients: Only $0.5\%$ of patients in the test set have cancer and a logistic regresssion model gives $99\%$ accuracy. <br/> <strong>Breakdown:</strong> <ul> <li> $P$ is the number of correctly predicted cases. </li> </ul> <strong>Idea:</strong> We check the accuracy percentage of two models: the logistic regression and a simple algorithm always predicts "No Cancer". <table class="language-python"> <tr> <th></th> <th>Logistic Regression</th> <th>Always-Predict-No-Cancer Algorithm</th> </tr> <tr> <th scope="row">Accuracy Values</th> <td> $P = 99\%$ <br/> $9900$ patients are correctly predicted. </td> <td> $P = 100 - 0.05 = 99.5\%$ <br/> $9950$ patients are correctly predicted with no cancer. </td> </tr> <tr> <th scope="row">Pros and Cons</th> <td>Obtain lower accuracy but it possibly catches bothe cancer and no-cancer cases.</td> <td>Look "perfect" by accuracy but it never catches a single cancer case.</td> </tr> <tr> <th scope="row">Note</th> <td colspan="2">CANNOT conclude the model with higher (naive) acccuracy is better.</td> </tr> </table> </div> </details> <p><br/></p> <p><strong>Precision</strong> is the ratio of all the model’s positive predictions that are actually positive. In the example above, it is the fraction between correctly predicted cancer cases and all the predicted cancer cases.</p> <p><strong>Recall</strong> is the proportion of all the actual positive cases that are correctly predicted positive. In this example, it is the percentage of correctly predicted cancer cases over all the real cancer cases.</p> <p><strong>$F_1$ score</strong> is a metric that combines both precision and recall into a single number using the <strong>harmonic mean</strong>. This mean penalizes the imbalance of a dataset like the simple algorithm example earlier. That is, <em>$F_1$ score</em> will <em>only</em> be high if <em>both precision and recall are high</em>. We use <em>$F_1$ score</em> to <em>evaluate a model’s performance</em> since it delivers a single quantitative measure that balances precision and recall.</p> <table> <tr> <th colspan="2" rowspan="2">Dataset</th> <th colspan="2">Real Label</th> <th rowspan="2">Note</th> </tr> <tr> <th>Positive</th> <th>Negative</th> </tr> <tr> <th scope="row" rowspan="2">Predicted Label</th> <th scope="row">Positive</th> <td>True Positive $TP$</td> <td>False Positive $FP$</td> <td>$$\text{Precision} = \frac{TP}{TP + FP}$$</td> </tr> <tr> <th scope="row">Negative</th> <td>False Negative $FN$</td> <td>True Negative $TN$</td> <td></td> </tr> <tr> <th scop="row" colspan="2">Note</th> <td>$$\text{Recall} = \frac{TP}{TP + FN}$$</td> <td></td> <td> $$\begin{align*} &amp;F_1 \text{ Score} \\ &amp;= 2 \times \frac{\text{Precision } \times \text{ Recall}}{\text{Precision } + \text{ Recall}} \end{align*}$$ </td> </tr> </table> <p><br/></p> <p>The following table illustrates the kind of problem we may encounter if we only make guesses from precision and recall values. We can choose the algorithm with <em>higher $F_1$ score</em> for a <em>better model performance</em>.</p> <table> <tr> <th></th> <th>Precision (P)</th> <th>Recall (R)</th> <th>Issue</th> <th>$F_1$ Score</th> </tr> <tr> <th scope="row">Algorithm 1</th> <td>$0.5$</td> <td>$0.4$</td> <td>$0.5$ says only half of the people flagged as cancer patients truly have cancer and the model predict $40\%$ cancer cases correctly. <br/> This suggests that the model may do too much guess work rather than logical prediction and give mediocre overall.</td> <td>$0.444$</td> </tr> <tr> <th scope="row">Algorithm 2</th> <td>$0.7$</td> <td>$0.1$</td> <td>$0.7$ means $70\%$ of predicted cancer cases actually have cancer but the model misses $90\%$ of people who really have cancer. <br/> This suggests that the model may be too careful when identify cancer cases, meaning it flags excessive number of non-cancer patients.</td> <td>$0.175$</td> </tr> <tr> <th scope="row">Algorithm 3</th> <td>$0.02$</td> <td>$1$</td> <td>$1$ means the model can catch every single cancer patient but out of all the people it flags as having cancer, $98\%$ are actually healthy. <br/> This suggests that the model may predict all the patients have cancer, which produces many false alarms.</td> <td>$0.039$</td> </tr> </table> <p><br/></p> <h3 id="weight-in-knn"><strong>Weight in KNN</strong></h3> <p>In the previous section, we mentioned the issue with the classic KNN algorithm where a tie in voting happens at step 5, especially $K$ is even. In this case, there is no specific instruction on which label should be chosen as two of them are both most frequent.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="https://github.com/huong-vo/huong-vo.github.io/blob/main/assets/aio2025/M03W02/friday_knn_keven_issue-480.webp 480w,https://github.com/huong-vo/huong-vo.github.io/blob/main/assets/aio2025/M03W02/friday_knn_keven_issue-800.webp 800w,https://github.com/huong-vo/huong-vo.github.io/blob/main/assets/aio2025/M03W02/friday_knn_keven_issue-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="https://github.com/huong-vo/huong-vo.github.io/blob/main/assets/aio2025/M03W02/friday_knn_keven_issue.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 300px; max-height: 300px; " alt="unmatched accuracy scores using Sklearn versus a modified KNN algorithm when $K$ is even" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption"><em>Figure 5.</em> Unmatched accuracy scores using *scikit-learn* versus a *modified KNN* when $K$ is even</figcaption> </figure> <p>To address this problem, <strong>scikit-learn</strong> allows us to provide some additional parameter to control how ties are resolved in the case of some labels making an <strong>equal contribution</strong> among the $K$ nearest neighbors. This is to stress on each label’s <strong>amount of say</strong>—its influence compared to others.</p> <p>The addition parameter discussed here is <code class="language-plaintext highlighter-rouge">weights</code> in <code class="language-plaintext highlighter-rouge">classifier = KNeighborsClassifier(n_neighbors=8, weights = 'WeightType')</code>. Common weight types are</p> <ul> <li><strong>uniform</strong>: default weights.</li> <li><strong>distance</strong> <ul> <li>Closer neighbors have more influence than farther ones.</li> <li>Weight is typically inverse of distance, that is, $\sum \frac{1}{d}$ where d is the distance from the test point to a training point.</li> </ul> </li> <li><strong>User-defined</strong>.</li> </ul> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> Write a function to compute Euclidean distance between two data points (vectors) using a <code>for</code> loop. <br/> <strong>Exercise 2:</strong> Do the same as exercise 1 for $x\_test = (2.4, 0.8)$ and $x\_test = (2.6, 0.8)$ when <ol> <li> $k = 1$ </li> <li> $k = 3$ </li> <li> $k = 5$ </li> </ol> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features-480.webp 480w,/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features-800.webp 800w,/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 300px; max-height: 300px; " alt="knn procedure exercise 2" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption"></figcaption> </figure> <strong>Exercise 3:</strong> Write code for both exercises above. <br/> <strong>Exercise 4:</strong> Use the following table and step-by-step procedure with Euclidean distance to predict the salary for $experience = 5.3$ when <ol> <li> $k = 2$ </li> <li> $k = 3$ </li> <li> $k = 4$ </li> </ol> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features-480.webp 480w,/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features-800.webp 800w,/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 300px; max-height: 300px; " alt="knn procedure exercise 2" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption"></figcaption> </figure> <strong>Exercise 5:</strong> Write code for exercise 4 above. </div> </details> <p><br/></p> <hr/> <figure> <div style="display: flex; flex-wrap:wrap; column-gap: 20px; justify-content: center"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features-480.webp 480w,/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features-800.webp 800w,/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 300px; max-height: 300px; " alt="petal length values are much greater than petal width, increasing the distances along with them, so the distances increase according to and are determined by length" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">Petal length values are much greater than petal width. So, the distances increase according to and are determined more by length.</figcaption> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W02%5Ctuesday_knn_procedure_issue-480.webp 480w,/assets%5Caio2025%5CM03W02%5Ctuesday_knn_procedure_issue-800.webp 800w,/assets%5Caio2025%5CM03W02%5Ctuesday_knn_procedure_issue-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W02%5Ctuesday_knn_procedure_issue.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 320px; max-height: 450px; " alt="etal length and petal width have different units" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">Petal length and petal width have different units</figcaption> </figure> </div> <figcaption style="text-align: center; width: 100%; font-size:0.875em; margin-top:-25px;"> <em>Figure 1.</em> Examples of issue without scaling </figcaption> </figure> <h2 id="knn-for-classification-tasks"><strong>KNN for Classification Tasks</strong></h2> <details class="bordered-block"> <summary> <strong>Text Classification with KNN</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Problem statement:</strong> Solve the task of text classification using KNN with the given procedure. <br/> <strong>Breakdown:</strong> <ul> <li> Input is a corpus of texts or strings and a random string that is not in the corpus. </li> <li> Output is a corpus string that is potentially closet to the random string. </li> </ul> <strong>Idea:</strong> We follow the procedure of the KNN algorithm. <ol> <li> To build a model, we follow a <strong>pipeline</strong> which is a complete model building process. </li> <ol> <li> <em>Obtain message input</em> with its label values. </li> <li> <em>Preprocessing: convert</em> message to numeric values like vectors. </li> <li> <em>Build vocabulary</em>. </li> <li> <em>Create features</em>. </li> <li> <em>Pass to a KNN model</em>. </li> <li> <em>Obtain results</em>. </li> </ol> <li> We can handle preprocessing to build vocabulary via two approaches: <strong>Bag-of-Words (BoW)</strong> and <strong>Term Frequency-Inverse Document Frequency (TF-IDF)</strong>. </li> </ol> :pushpin: <strong>Note:</strong> Essentially, the KNN algorithm stays similar for all types of data: image, sound, tabular, and time series data. But we need to figure out how to work with given data to induce real feature values. </div> </details> <p><br/> <em>BoW</em> is simpler and more basic approach to text representation than <em>TF-IDF</em>. BoW only considers word occurences while TF-IDF provides more <em>informative</em> representation of text—taking into account both word importance and context within the entire dataset. <em>TF-IDF is generally preferred</em>. We’ll delve deeper into the two techniques right away.</p> <details class="bordered-block"> <summary> <strong>Bag-of-Words (BoW)</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Problem statement:</strong> We have the following corpus. <table class="language-python"> <tr> <th>Doc</th> <th>Label</th> </tr> <tr> <td>you reap what you sow</td> <td class="centered_td">1</td> </tr> <tr> <td>beggars can't be choosers</td> <td class="centered_td">1</td> </tr> <tr> <td>like bees to honey</td> <td class="centered_td">1</td> </tr> <tr> <td>bite the hands that feed you</td> <td class="centered_td">0</td> </tr> <tr> <td>a taste of your own medicine</td> <td class="centered_td">0</td> </tr> <tr> <td>burn your bridges behind you</td> <td class="centered_td">0</td> </tr> </table> <br/> <strong>Idea:</strong> BoW is to investigate distances between two sentences based on word occurences. <ol> <li> <strong>Tokenization</strong> splits text into a list of smaller parts or separate words. </li> <li> Initialize an empty vocabulary. Add new words from tokenized corpus into the vocabulary. </li> Vocabulary = <li> Take a random string: <em>"you can't pick and choose"</em>. </li> <li> Convert the string to a numeric value by creating a <strong>BoW</strong> from the string and assigning a number to each of them. Two common ways are </li> <ul> <li> <strong>Binary BoW:</strong> Words in the string are assigned $1$ and others are $0$. </li> <li> <strong>(Complete) BoW:</strong> Each value is the number of its' word <em>occurrency</em>. </li> </ul> We use the <em>(complete) BoW</em> for this problem. <ul> <li> From here, we build a matrix where each row is a word in the constructed vocabulary, each column is a sentence from the corpus, and each cell of a column is the BoW value of the corresponding sentence. Corpus sentences now become <em>training vectors/points</em>. </li> <li> With the random string, we can build a <em>test vector/point</em>. </li> </ul> <s>DEMONSTRATION for matrix</s> </ol> <br/> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Cwednesday_1Dinterpolation_nearest_neighbor-480.webp 480w,/assets%5Caio2025%5CM03W01%5Cwednesday_1Dinterpolation_nearest_neighbor-800.webp 800w,/assets%5Caio2025%5CM03W01%5Cwednesday_1Dinterpolation_nearest_neighbor-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Cwednesday_1Dinterpolation_nearest_neighbor.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 320px; max-height: 450px; " alt="" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption"></figcaption> </figure> </div> </details> <details class="bordered-block"> <summary> <strong>Term Frequency-Inverse Document Frequency (TF-IDF)</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Problem statement:</strong> We use the same corpus as in BoW. We can also use this approach to do spam email detection. <br/> <strong>Breakdown:</strong> <ul> <li> A document is a sentence/ an email. </li> <li> The corpus is a collection of documents. </li> </ul> <strong>Idea:</strong> TF-IDF is to compute the importance level of a word or phrase relative to its parent sentence/email and the entire corpus. Thus, TF-IDF helps identify words that are significant in a specific document but maybe rare across all the documents. <ol> <li> We build the same matrix for the entire corpus as in Bag-of-Words technique. </li> <li> We build three other matrices: </li> <ul> <li> <strong>TF matrix:</strong> Each cell is the corresponding TF value of the row word and the column document. </li> <li> <strong>IDF matrix:</strong> Each cell is the corresponding IDF value of the row word and the column document. </li> <li> <strong>TF-IDF matrix:</strong> Each cell is the corresponding TF-IDF value of the row word and the column document. </li> <li> <strong>Normalized TF-IDF matrix:</strong> Each cell is the corresponding normalized TF-IDF value of the row word and the column document. </li> </ul> <li> We build vectors for test documents. </li> <li> <strong>Term Frequency (TF)</strong> measures how often a term/word appears in a document, that is how important a word is in a document. </li> $$\begin{align*} TF_{(t,d)} &amp;= count(t,d) \\ &amp;= \frac{\text{Number of times word$_\textbf{i}$ appears in a document}}{\text{Total number of words in the document}} \end{align*}$$ or <em>in practice</em>, $$TF_{(t,d)} = \log{(count(t,d) + 1)}$$ However, a word with a high occurence may not actually be of large importance, for example "the", "and", "are", and "is". We need additional step to handle this situation. <li> <strong>Inverse Document Frequency (IDF)</strong> measures how important a word is across the entire corpus. </li> $$\begin{align*} IDF_{t} &amp;= \log \frac{\text{Total number of documents}}{\text{Number of documents containing word$_\textbf{i}$}} \\ &amp;= \frac{N}{DF_t} \end{align*}$$ or <em>in practice</em>, $$\begin{align*} IDF_{t} &amp;= \log \frac{\text{Total number of documents + 1}}{\text{Number of documents containing word$_\textbf{i}$ + 1}} + 1 \\ &amp;= \frac{N + 1}{DF_t + 1} + 1 \end{align*}$$ Adding $1$ is to avoid $0$ as a result since it could kill of the importance of a word and avoid division by zero. This technique is called <strong>smoothing</strong> used in packages like <strong>scikit-learn</strong>. <li> <strong>TF-IDF</strong> computes the importance of a word in an email relative to the entire collection. </li> $$TF-IDF_{(t,d)} = TF_{(t,d)} \times IDF_{t}$$ This multiplication helps compensate for the disproportionate influence of frequently occuring but uninformative words and ensure the more meaningful features contribute appropriately to the distance calculations. <br/> This next step is not required. The TF-IDF matrix contains many zeros and possiblly much greater values, so we need to normalize them like we perform scaling in KNN. This helps avoid bias from document length. <li> <strong>Normalized TF-IDF</strong> takes the unit vector of each TF-IDF vector. <strong>L2 norm</strong> is also known as <strong>Euclidean norm</strong>. </li> $$L2\_norm(TF-IDF) = \frac{TF-IDF}{\left \| TF-IDF \right \|_2} = \frac{\langle x_1, \cdots, x_n}{\left( \sum_i=1^n x_i^2\right)^\frac{1}{2}}$$ <s>DEMONSTRATION for matrix</s> </ol> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Cwednesday_1Dinterpolation_nearest_neighbor-480.webp 480w,/assets%5Caio2025%5CM03W01%5Cwednesday_1Dinterpolation_nearest_neighbor-800.webp 800w,/assets%5Caio2025%5CM03W01%5Cwednesday_1Dinterpolation_nearest_neighbor-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Cwednesday_1Dinterpolation_nearest_neighbor.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 320px; max-height: 450px; " alt="" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption"></figcaption> </figure> </div> </details> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> Given a corpus of two documents: doc 1 = "apple banana" and doc 2 = "banana banana". Compute TF, IDF, and TF-IDF (both theoretical and practical). <br/> <strong>Exercise 2:</strong> Write code to complete the example for each approach. </div> </details> <hr/> <h2 id="case-study-code-implementation"><strong>Case Study: Code Implementation</strong></h2> <p>We primarily use the <strong>scikit-learn</strong> library for ML problems.</p> <p><strong>Problem statement:</strong> <em>Build a system to predict whether a tumor is maglinant or benign based on real-valued information using KNN.</em></p> <p><strong>Set Up</strong></p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># for a tabular dataset
</span><span class="kn">import</span> <span class="n">pandas</span> <span class="k">as</span> <span class="n">pd</span>

<span class="c1"># this dataset is available in the package, so we just need to import it
</span><span class="kn">from</span> <span class="n">sklearn.datasets</span> <span class="kn">import</span> <span class="n">load_breast_cancer</span>

<span class="c1"># 
</span><span class="kn">from</span> <span class="n">sklearn.model_selection</span> <span class="kn">import</span> <span class="n">train_test_split</span>

<span class="c1"># for scaling data (mentioned above)
</span><span class="kn">from</span> <span class="n">sklearn.preprocessing</span> <span class="kn">import</span> <span class="n">StandardScaler</span>

<span class="c1"># KNN algorithm in the package
</span><span class="kn">from</span> <span class="n">sklearn.neighbors</span> <span class="kn">import</span> <span class="n">KNeighborsClassifier</span>

<span class="c1"># for classification tasks and assessing how accurate our model is
</span><span class="kn">from</span> <span class="n">sklearn.metrics</span> <span class="kn">import</span> <span class="n">accuracy_score</span><span class="p">,</span> <span class="n">classification_report</span>
</code></pre></div></div> <p>The <strong>pipeline</strong> for a ML problem follows the basic procedure below. In practice, there can more steps to work through.</p> <ol> <li>Data input</li> <li>Data preprocessing</li> <li>Data spliting: <ul> <li>Training data</li> <li>Test data</li> </ul> </li> <li>Data training</li> <li>Model evaluation</li> </ol> <p><strong>Data Input</strong></p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># load and read the dataset
</span><span class="n">cancer</span> <span class="o">=</span> <span class="nf">load_breast_cancer</span><span class="p">()</span>

<span class="c1"># convert the feature columns to a DataFrame
</span><span class="n">X</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nc">DataFrame</span><span class="p">(</span><span class="n">cancer</span><span class="p">.</span><span class="n">data</span><span class="p">,</span> <span class="n">columns</span><span class="o">=</span><span class="n">cancer</span><span class="p">.</span><span class="n">feature_names</span><span class="p">)</span>

<span class="c1"># convert the label column to a Series
</span><span class="n">y</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nc">Series</span><span class="p">(</span><span class="n">cancer</span><span class="p">.</span><span class="n">target</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="sh">'</span><span class="s">target</span><span class="sh">'</span><span class="p">)</span>
</code></pre></div></div> <p><del>DEMONSTRATION FOR FIRST FIVE ROWS</del></p> <p>Since the target contains non-numerical values: benign and malignant, we need to assign numeric values to them: $1$ for benign and $0$ for malignant.</p> <p><strong>Data Spliting</strong> <br/> We mentioned splitting a dataset (label included) into two sets: training and test data. But in practice, a dataset is usally divided into three following sets:</p> <ul> <li>Training data</li> <li>Test data</li> <li><em>Evaluation data:</em> we use this set to evaluate how accurate the ML model is—checking how close its predicted label values is to its actual label values, then check the same for test data.</li> </ul> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">val_size</span> <span class="o">=</span> <span class="mf">0.2</span>
<span class="n">test_size</span> <span class="o">=</span> <span class="mf">0.125</span>
<span class="n">random_state</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">is_shuffle</span> <span class="o">=</span> <span class="bp">True</span>

<span class="c1"># split training and test data
</span><span class="n">X_train</span><span class="p">,</span> <span class="n">X_test</span><span class="p">,</span> <span class="n">y_train</span><span class="p">,</span> <span class="n">y_test</span> <span class="o">=</span> <span class="nf">train_test_split</span><span class="p">(</span>
    <span class="n">X</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> 
    <span class="n">test_size</span><span class="o">=</span><span class="n">test_size</span><span class="p">,</span> 
    <span class="n">shuffle</span><span class="o">=</span><span class="n">is_shuffle</span><span class="p">,</span>
    <span class="n">random_state</span><span class="o">=</span><span class="n">random_state</span><span class="p">)</span>

<span class="c1"># split training and evaluation data
</span><span class="n">X_train</span><span class="p">,</span> <span class="n">X_val</span><span class="p">,</span> <span class="n">y_train</span><span class="p">,</span> <span class="n">y_val</span> <span class="o">=</span> <span class="nf">train_test_split</span><span class="p">(</span>
    <span class="n">X</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> 
    <span class="n">test_size</span><span class="o">=</span><span class="n">val_size</span><span class="p">,</span> 
    <span class="n">shuffle</span><span class="o">=</span><span class="n">is_shuffle</span><span class="p">,</span>
    <span class="n">random_state</span><span class="o">=</span><span class="n">random_state</span><span class="p">)</span>
</code></pre></div></div> <p><strong>Data Training</strong> <br/></p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># scale data
# call scaling function
</span><span class="n">scaler</span> <span class="o">=</span> <span class="nc">StandardScaler</span><span class="p">()</span>

<span class="c1"># scale both training and test data
</span><span class="n">X_train_scaled</span> <span class="o">=</span> <span class="n">scaler</span><span class="p">.</span><span class="nf">fit_transform</span><span class="p">(</span><span class="n">X_train</span><span class="p">)</span>
<span class="n">X_test_scaled</span> <span class="o">=</span> <span class="n">scaler</span><span class="p">.</span><span class="nf">transform</span><span class="p">(</span><span class="n">X_test</span><span class="p">)</span>
</code></pre></div></div> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># initialize k
</span><span class="n">k</span> <span class="o">=</span> <span class="mi">5</span>

<span class="c1"># call KNN algorithm
</span><span class="n">knn_classifier</span> <span class="o">=</span> <span class="nc">KNeighborsClassifier</span><span class="p">(</span><span class="n">n_neighbors</span><span class="o">=</span><span class="n">k</span><span class="p">)</span>

<span class="c1"># train data
</span><span class="n">knn_classifier</span><span class="p">.</span><span class="nf">fit</span><span class="p">(</span><span class="n">X_train_scaled</span><span class="p">,</span> <span class="n">y_train</span><span class="p">)</span>
</code></pre></div></div> <p><del>DEMONSTRATION IN SLIDE, ONLY TAKE 2 FOR VISUALIZATION, SHOULD BE 30 BUT THERE IS NO WAY TO VISUALIZE 30 DIMENSIONS</del></p> <p><strong>Model Evaluation</strong> <br/> This step plays a very important role during the model development. Depending on the result, we can tune parameters, try different metrics, process the data differently or other methods. Once we get high-enough accuracy, we can start testing on test data.</p> <p>You can think of evaluation data as a practice exam while test data is a real exam.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># make evaluation on evaluation data
</span>
<span class="c1"># test on evaluation data
</span><span class="n">y_pred</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="nf">predict</span><span class="p">(</span><span class="n">X_val_scaled</span><span class="p">)</span>

<span class="c1"># check the model's accuracy on evaluation data
</span><span class="n">accuracy</span> <span class="o">=</span> <span class="nf">accuracy_score</span><span class="p">(</span><span class="n">y_val</span><span class="p">,</span> <span class="n">y_pred</span><span class="p">)</span>

<span class="c1"># obtain precision, recall, and f1-score values
</span><span class="n">report</span> <span class="o">=</span> <span class="nf">classification_report</span><span class="p">(</span><span class="n">y_val</span><span class="p">,</span> <span class="n">y_pred</span><span class="p">)</span>
</code></pre></div></div> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># make evaluation on test data
</span>
<span class="c1"># test on test data
</span><span class="n">y_pred</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="nf">predict</span><span class="p">(</span><span class="n">X_test_scaled</span><span class="p">)</span>

<span class="c1"># check the model's accuracy on test data
</span><span class="n">accuracy</span> <span class="o">=</span> <span class="nf">accuracy_score</span><span class="p">(</span><span class="n">y_test</span><span class="p">,</span> <span class="n">y_pred</span><span class="p">)</span>

<span class="c1"># obtain precision, recall, and f1-score values
</span><span class="n">report</span> <span class="o">=</span> <span class="nf">classification_report</span><span class="p">(</span><span class="n">y_test</span><span class="p">,</span> <span class="n">y_pred</span><span class="p">)</span>
</code></pre></div></div> <p>We can write our own code for checking the accuracy above using the formula</p> \[Accuracy = \frac{\text{Numer of correct predictions}}{\text{Total numebr of predictions}}.\] <p>The same goes for <em>precision</em>, <em>recall</em>, and <em>F1-Score</em> values.</p> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> Write code to display the first five rows, data information, statistics information, numeric value assignments, <br/> <strong>Exercise 2:</strong> What are the accuracy scores for evaluation and test data? </div> </details> <hr/> <h2 id="speech_balloon-faq">:speech_balloon: <strong>FAQ</strong></h2> <p>Here are some good questions asked during class. The answers to these questions came from either some admin, TA, STA, or members of AIO2025.</p> <table> <thead> <tr> <th>Questions</th> <th>Answers</th> </tr> </thead> <tbody> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td> </td> </tr> <tr> <td> </td> <td> </td> </tr> </tbody> </table> <p><br/></p> ]]></content><author><name></name></author><category term="AI"/><category term="coding"/><category term="AIO"/><category term="linear-algebra"/><summary type="html"><![CDATA[KNN, supervised learning, classification, regression, K-D Tree]]></summary></entry><entry><title type="html">Machine Learning 101</title><link href="https://huong-vo.github.io/blog/2025/KMeans101/" rel="alternate" type="text/html" title="Machine Learning 101"/><published>2025-08-12T00:00:00+00:00</published><updated>2025-08-12T00:00:00+00:00</updated><id>https://huong-vo.github.io/blog/2025/KMeans101</id><content type="html" xml:base="https://huong-vo.github.io/blog/2025/KMeans101/"><![CDATA[<p><strong>Acknowledgement:</strong> This post is based mainly on Dr. Quang-Vinh Dinh’s presentation and slides and partly on on TA, STA, and peers’ responses from the course AIO2025 offered by AI VIETNAM.</p> <p><strong>Relevance:</strong> The audience of this lecture were those who have read <a href="/blog/2025/KNN-pre101/">Machine Learning 101 Warm-up</a>, had some coding experience, and known a bit of AI knowledge.</p> <p>In today’s post, we’ll explore K-Means and Unsupervised Learning. We’ll also discuss some applications in AI including tabular, image, and text data.</p> <hr/> <p>A dataset in Supervised Learning (SL) comes with two parts: X (the features) and Y (the labels). SL is used to solve classification and regression problems where we need the input data X and their results Y in order for a model to learn. In Unsupervised Learning (USL), a dataset only has X. Since there is no Y, USL cannot solve problems like classification or regression. Instead it can work on clustering problems which aim to group data points based on some property/condition; different properties/features can give different output.</p> <h2 id="motivation"><strong>Motivation</strong></h2> <p>Let’s discuss a real-life problem. We have a dataset of sales from customers; this dataset does not give any information on customers, that is, is not labeled. The data is scattered and the whole dataset is not compactly represented.</p> <p>The questions become, <strong>“How can we extract some useful information? More specifically, how can we classify customers into VIP and normal groups?”</strong>.</p> <p>This is a typical example for the <strong>clustering problem</strong>.</p> <p>For this particular question, we can think of the way and amount each group spends. The potential VIP tends to spend much more than a normal customer. Qualitatively, this can result in a clear divider between two groups. Visually, the customers are grouped into two sets which do not intersect/overlap with each other.</p> <p>Nonetheless, to conclude it quantitatively, we need to have numbers to support our conjecture. Let’s experiment with a sample dataset.</p> <details class="bordered-block"> <summary> <strong>Sample</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Problem statement:</strong> A sample set of spending amounts is $\{\$6, \$8, \$10, \$12, \$36, \$40, \$41\}$. <br/> <strong>Breakdown:</strong> <ul> <li> $C_1$ and $C_2$ are the centers of normal and VIP groups, respectively. </li> <li> $R_1$ and $R_2$ are the radii of normal and VIP groups, respectively. </li> <li> $S$ is the <strong>signal</strong> of the dataset based on grouping/clustering. </li> </ul> <strong>Idea:</strong> Experiment different ways to form groups from the dataset, paraphrase them mathematically, and conclude which one may be best. This experimental method is built on the idea from statistics---the distribution of a dataset. <ol> <li> <strong>Separate</strong> the dataset into <strong>different clusters</strong>. </li> <li> <strong>Determine $C$</strong> which acts as a cluster's mean. </li> <li> <strong>Determine $R$</strong> which represents how spread out each cluster is, that is, loosely acts as a group's standard deviation. </li> <li> <strong>Determine $S$</strong> which represents how spread out the entire dataset is from the centers. </li> <li> <strong>Compare $S$</strong> and <strong>choose one with the smallest signal</strong>. The smaller it is, the more compact the clusters are, the more meaningful the grouping is potentially. </li> We pick the first grouping for $2 &lt; 4.57 &lt; 5.73$. </ol> From the conclusion above and the formulae in the table below, we <em>observe</em> that <strong>a data point will belong to a set if it is closer to its center than other centers</strong>. $$\begin{align} \label{observation-kmeans-clustering} Set_i = \{x_p: |x_p - C_i| \le |x_p - C_j| \forall j\} \end{align}$$ <table class="language-python"> <tr> <th>No</th> <th>Groups</th> <th> Center of the Cluster $$C = \frac{\text{Sum of values}}{\text{Number of values}} = \frac{\sum_{p=1}^{k} x_p}{k}$$ </th> <th> Radius of the Cluster $$R = \frac{\text{Sum of distances from values to center}}{\text{Number of values}} = \frac{\sum_{p=1}^{k} |x_p - C|}{k}$$ </th> <th> Signal of the Clusters $$S = \frac{\text{Sum of radii}}{\text{Number of groups}}$$ </th> </tr> <tr> <td rowspan="2">1</td> <td> Normal $$\{6, 8, 10, 12\}$$ </td> <td>$$C_1 = \frac{6 + 8 + 10 + 12}{4} = 9$$</td> <td>$$R_1 = \frac{|6 - 9| + |8 - 9| + |10 - 9| + |12 - 9|}{4} = 2$$</td> <td rowspan="2"> $$\begin{align*} &amp;S = \frac{R_0 + R_1}{2} \\ &amp;= \frac{2 + 2}{2} = 2 \end{align*}$$ </td> </tr> <tr> <td> VIP $$\{36, 40, 41\}$$ </td> <td>$$C_2 = \frac{36 + 40 + 41}{3} = 39$$</td> <td>$$R_2 = \frac{|36 - 39| + |40 - 39| + |41 - 39|}{4} = 2$$</td> </tr> <tr> <td rowspan="2">2</td> <td> Normal $$\{6, 8, 10\}$$ </td> <td>$$C_1 = \frac{6 + 8 + 10}{3} = 8$$</td> <td>$$R_1 = \frac{|6 - 9| + |8 - 9| + |10 - 9|}{3} = \frac{4}{3} \approx 1.33$$</td> <td rowspan="2"> $$\begin{align*} &amp;S = \frac{R_0 + R_1}{2} \\ &amp;\approx \frac{1.33 + 10.13}{2} = 5.73 \end{align*}$$ </td> </tr> <tr> <td> VIP $$\{12, 36, 40, 41\}$$ </td> <td>$$C_2 = \frac{12 + 36 + 40 + 41}{4} = 32.25$$</td> <td>$$R_2 = \frac{|12 - 32.25| + |36 - 32.25| + |40 - 32.25| + |41 - 32.25|}{4} = 10.13$$</td> </tr> <tr> <td rowspan="2">3</td> <td> Normal $$\{6, 8, 10, 12\}$$ </td> <td>$$C_1 = \frac{6 + 8 + 10 + 12 + 36}{5} = 14.4$$</td> <td>$$R_1 = \frac{|6 - 14.4| + |8 - 14.4| + |10 - 14.4| + |12 - 14.4| + |36 - 14.4|}{5} = 8.64$$</td> <td rowspan="2"> $$\begin{align*} &amp;S = \frac{R_0 + R_1}{2} \\ &amp;\approx \frac{8.64 + 0.5}{2} = 4.57 \end{align*}$$ </td> </tr> <tr> <td> VIP $$\{36, 40, 41\}$$ </td> <td>$$C_2 = \frac{40 + 41}{2} = 40.5$$</td> <td>$$R_2 = \frac{|40 - 40.5| + |41 - 40.5|}{2} = 0.5$$</td> </tr> </table> <br/> <figure style="margin-top: 0; ping-top: 0;"> <iframe src="https://www.geogebra.org/classic/eweqcfvf?embed" title="Interactive GeoGebra applet one-dimensional clustering demo." frameborder="0" height="700px" width="100%" style="border: 1px solid #ddd; margin-top: 0; ping-top: 0;" allowfullscreen="">Your browser does not support iframes. View the interactive applet <a href="https://www.geogebra.org/classic/eweqcfvf" target="_blank" rel="noopener">here</a>.</iframe> <figcaption style="font-size:0.875em"> <em>Figure 1.</em> Drag points around for different datasets. Use the sliders for each point to change its group assignment. The applet updates clusters, their centers, and average radii instantly. Click on the symbol in the bottom right corner to open the applet in full-screen mode. Drag boxes and sliders for your convenience. If the applet does not load, you can open it directly <a href="https://www.geogebra.org/classic/eweqcfvf" target="_blank" rel="noopener">here</a>.</figcaption> </figure> </div> </details> <p><br/></p> <p>:pushpin: <strong>Note:</strong> Even though the solution presented in the Sample works for a toy dataset, some problems needs addressing with this approach for it to work in real-world applications.</p> <ul> <li>Manually grouping and trying many possible of them for an actual dataset are very inefficient. <ul> <li>There is no clear way on how to make a grouping better.</li> </ul> </li> <li>Unsystematically forming/guessing a grouping leads to a center biased by the grouping. <ul> <li>It’s hardly possible to get a proper center and its cluster.</li> </ul> </li> </ul> <p>To uplift the solution we discussed above, we can <em>reverse the process</em> by initially picking centers and then forming clusters.</p> <details class="bordered-block"> <summary> <strong>Improved Sample</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Problem statement:</strong> A sample set of spending amounts is $\{\$6, \$8, \$10, \$12, \$36, \$40, \$41\}$. <br/> <strong>Breakdown:</strong> The formulae for the following values stay the same. <ul> <li> $C_i$ is the center of a group. </li> <li> $R_i$ is the radius of a group. </li> <li> $Set_i$ is a group. </li> <li> $S$ is the signal of the dataset based on grouping/clustering. </li> </ul> <strong>Idea:</strong> We guess centers and then form their clusters. We repeat this process until groups stop changing. <ol> <li> <strong>Randomly select $C_i$'s</strong>. </li> In this example, let's initialize $C_1 = 8$ and $C_2 = 29$. <li> <strong>Assign a data point to its nearest $C_i$</strong>. </li> Based on \eqref{observation-kmeans-clustering}, we have $$Set_1 = \{6, 8, 10, 12\}$$ and $$Set_2 = \{36, 40, 41\}.$$ <li> <strong>Update $C_i$'s</strong> based on new information. </li> Now with two clusters above, $C_1 = 9$ and $C_2 = 39$. <li> <strong>Determine $R_i$'s</strong> based on the new $C_i$'s. </li> $R_1 = 2$ and $R_2 = 2$ <li> <strong>Determine $S$</strong>. </li> $S = 2$ since $K = 2$. <li> <strong>Reuse $C_i$'s and repeat steps 2-4 until $S$ remains unchanged</strong>, that is, the clusters, centers, and radii stay stable. </li> In this particular example, all the values do not change after being recalculated. So, this is our wanted grouping. </ol> <figure style="margin-top: 0; ping-top: 0;"> <iframe src="https://www.geogebra.org/classic/gcwpdwp4?embed" title="Interactive GeoGebra applet one-dimensional K-Means clustering demo." frameborder="0" height="700px" width="100%" style="border: 1px solid #ddd; margin-top: 0; ping-top: 0;" allowfullscreen="">Your browser does not support iframes. View the interactive applet <a href="https://www.geogebra.org/classic/gcwpdwp4" target="_blank" rel="noopener">here</a>.</iframe> <figcaption style="font-size:0.875em"> <em>Figure 2.</em> Drag points around and click the "Update Clusters" for automatic group reassignment. The applet updates clusters, their centers, and average radii instantly. Click on the symbol in the bottom right corner to open the applet in full-screen mode. Drag boxes and sliders for your convenience. If the applet does not load, you can open it directly <a href="https://www.geogebra.org/classic/gcwpdwp4" target="_blank" rel="noopener">here</a>.</figcaption> </figure> </div> </details> <p><br/></p> <h2 id="k-means"><strong>$K$-Means</strong></h2> <p>The improved version of the example above follows the exact procedure for grouping $n$ observations into $K$ clusters based on the distance in $K$-Means.</p> <h3 id="procedure"><strong>Procedure</strong></h3> <p>We should always preprocess the data first. Then we can apply $K$-Means to a ML model in the following steps.</p> <ol> <li><em>Initialize</em> the value of $K$.</li> <li><em>Select random $K$ centroid</em>.</li> <li><em>Assign each data point to the closest centroid</em>.</li> <li><em>Update a new centroid</em> of each cluster.</li> <li><em>If not convergent, go to step 3. Otherwise, stop</em>.</li> </ol> <pre><code class="language-mermaid">flowchart LR
    A((&lt;strong&gt;Data processing&lt;/strong&gt;&lt;br&gt; select K&lt;/strong&gt;)) ---&gt; B((&lt;strong&gt;Select random&lt;br&gt; K centroids&lt;/strong&gt;)) ---&gt; C((&lt;strong&gt;Assign each data point&lt;br&gt; to the closest centroid&lt;/strong&gt;)) ---&gt; D((&lt;strong&gt;Update centroids&lt;/strong&gt;)) ---&gt;|Not Convergent| E((&lt;strong&gt;If not convergent,&lt;br&gt; go to step 3&lt;/strong&gt;))
    D ---&gt;|Convergent| F((&lt;strong&gt;Stop&lt;/strong&gt;))
</code></pre> <figcaption style="text-align: center; width: 100%; font-size: 0.875em; margin-top:-5px; margin-bottom:15px;"> <em>Figure 3.</em> K-Means Procedure </figcaption> <p>:pushpin: <strong>Note:</strong> $K$-Means always converges to a local minimum but does not guarantee convergence to global minimum (optimum). The result is dependent on the initialization.</p> <h3 id="k-means-algorithm"><strong>$K$-Means Algorithm</strong></h3> <p>You may question, <strong>“Why do we need to cluster data points? What do we do with them?</strong></p> <p>This question is on point. In USL, data is unlabeled, thus clustering data points into appropriate and suitable groups is the key to label the entire dataset based on the problem requirement.</p> <pre><code class="language-mermaid">%%{init: {'flowchart': {'curve': 'basis'}} }%%
flowchart TB
    %% Main Flow
    subgraph Algorithm ["Algorithm/Procedure"]
        direction TB

        subgraph IterateCentroids[" "]
            direction TB

            subgraph UpdateCentroid["Step 3: Update Centroids"]
                direction TB
                Recompute["Based on data points\n in the same centroid,\n re-compute the centroids"]:::dashedgreen
            end

            subgraph ClusterStep["Step 1: Find K Clusters"]
                direction BT
                RandomPickup["Random pickup"]:::dashed
                KCentroids1["K-Centroids"]:::core
                RandomPickup --&gt; KCentroids1
            end
        end

        subgraph LabelStep["Step 2: Labeling data points"]
            direction LR
            AssignLabels["Assign the label of the nearest\n centroid for all the data points"]:::dashedorange
            NLabelled["N-data points (labelled)"]:::coreorange
            AssignLabels --&gt; NLabelled
        end
    end

    subgraph Idea [Main Idea]
        direction TB
        Inputs["Inputs:\n• Unlabelled dataset\n• Number of Clusters (K)"]:::input
        Outputs["Outputs:\n• Centroids\n• Dataset labelled"]:::output
        Inputs ------&gt; Outputs
    end

    NLabelled -.-&gt;|"Update centroids"| UpdateCentroid
    UpdateCentroid --&gt; KCentroids1

    %% Cross connection
    KCentroids1 -.-&gt;|"Update labels"| AssignLabels

    %% Styling Definitions
    classDef input fill:#e6eef9,stroke:#7293ba,stroke-width:2px,font-family:serif;
    classDef output fill:#e6faeb,stroke:#628e6e,stroke-width:2px,font-family:serif;
    classDef dashed stroke:#7293ba,stroke-width:1px,stroke-dasharray: 6 4,fill:#f5faff,font-family:serif;
    classDef core fill:#c4e0fa,stroke:#3b6fa6,stroke-width:2px,font-weight:bold,font-family:serif;
    classDef dashedgreen fill:#f0faf4,stroke:#628e6e,stroke-width:2px,stroke-dasharray: 6 4,font-family:serif;
    classDef dashedorange fill:#faf2ea,stroke:#cd7a39,stroke-width:2px,stroke-dasharray: 6 4,font-family:serif;
    classDef coreorange fill:#fad1ae,stroke:#cd7a39,stroke-width:2px,font-weight:bold,font-family:serif;
    classDef labelHeader fill:#c4e0fa,stroke:#3b6fa6,stroke-width:2.5px,font-weight:bold,text-align:center;
    classDef orangeHeader fill:#ed8f33,stroke:#cd7a39,stroke-width:2.5px,font-weight:bold,color:#fff,text-align:center;
    class ClusterStep labelHeader
    class UpdateCentroid labelHeader
    class LabelStep orangeHeader
</code></pre> <figcaption style="text-align: center; width: 100%; font-size: 0.875em; margin-top:-5px; margin-bottom:15px;"> <em>Figure 4.</em> Detailed K-Means Algorithm </figcaption> <h3 id="formula"><strong>Formula</strong></h3> <p>We can write these steps in mathematical expressions.</p> <div class="math-box"> $\text{Consider } \{x_1, \cdots, x_n\} \text{ where } x_i \in \mathbb{R}^d.$ $\text{Consider an initial set of K centroids } c_0^{(t)}, \cdots, c_K^{(t)}.$ $\text{Let } S = \{S_0, \cdots, S_K\} \text{ be } K \text{ clusters.}$ <br/> <u><strong>Assignment step</strong></u> $$\begin{align} S_i^{(t)} = \{x_p : \left\Vert x_p - c_i^{(t)} \right\Vert ^2 \le \left\Vert x_p - c_j^{(t)} \right\Vert ^2 \forall j, 0 \le j \le k \} \end{align}$$ <u><strong>Update step</strong></u> $$\begin{align} c_i^{(t+1)} = \frac{1}{|S_i^{(t)}|} \sum_{x \in S_i^{(t)}} x \end{align}$$ </div> <p><br/></p> <p>The <strong>goal of the clustering problem</strong> is to make the groups <em>tight and compact</em> while still covering all the data. Mathematically, it can be expressed as follows.</p> <div class="math-box"> $\text{Consider } \{x_1, \cdots, x_n\} \text{ where } x_i \in \mathbb{R}^d.$ $\text{Consider an initial set of K centroids } c_0^{(t)}, \cdots, c_K^{(t)}.$ $\text{Let } S = \{S_0, \cdots, S_K\} \text{ be } K \text{ clusters.}$ <br/> $\text{For a given $K$, find the partition } S \text{ that minimizes the dispersion of data points within their clusters, i.e.,}$ $$\begin{align} \underset{S}{argmin} \sum_{i=0}^{k-1} \sum_{x \in S_i} \left\Vert x - c_i^{(t)} \right\Vert ^2 \end{align}$$ $\text{for } \left\Vert u \right\Vert _{L^2} ^2 = u_1^2 + \cdots + u_n^2.$ </div> <hr/> <h2 id="tabular-data"><strong>Tabular Data</strong></h2> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> Use the following Iris data table and step-by-step $K$-Means procedure with Euclidean distance to label data points. Fill in the table below for each iteraton. Take $K = 2$, $c_0 = 1.4$, and $c_1 = 1$. Check the answer with code snippet below. <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W02%5Cwednesday_kmeans_tabular_1feature-480.webp 480w,/assets%5Caio2025%5CM03W02%5Cwednesday_kmeans_tabular_1feature-800.webp 800w,/assets%5Caio2025%5CM03W02%5Cwednesday_kmeans_tabular_1feature-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W02%5Cwednesday_kmeans_tabular_1feature.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 170px; max-height: 250px; " alt="a 1D table for simplified iris dataset" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">A 1D table for simplified iris dataset</figcaption> </figure> <table class="language-python"> <tr> <th>Index</th> <th>Length</th> <th>Distance to $c_0$</th> <th>Distance to $c_1$</th> <th>Label</th> <th>New Centroids</th> </tr> <tr> <td class="centered_td">0</td> <td class="centered_td">1.4</td> <td></td> <td></td> <td></td> </tr> <tr> <td class="centered_td">1</td> <td class="centered_td">1</td> <td></td> <td></td> <td></td> </tr> <tr> <td class="centered_td">2</td> <td class="centered_td">1.5</td> <td></td> <td></td> <td></td> </tr> <tr> <td class="centered_td">3</td> <td class="centered_td">3.1</td> <td></td> <td></td> <td></td> </tr> <tr> <td class="centered_td">4</td> <td class="centered_td">3.8</td> <td></td> <td></td> <td></td> </tr> <tr> <td class="centered_td">5</td> <td class="centered_td">4.1</td> <td></td> <td></td> <td></td> </tr> </table> <br/> <strong>Exercise 2:</strong> Do the same as exercise 1 and make suitable tables for the age-expenditure table with <ol> <li> $K = 2$, $c_0 = (18, 80)$ and $c_1 = (22, 85)$$ </li> <li> $K = 3$, $c_0 = (22, 85)$, $c_1 = (30, 50)$, and $c_2 = (34, 64)$ </li> </ol> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W02%5Cwednesday_kmeans_tabular_2features-480.webp 480w,/assets%5Caio2025%5CM03W02%5Cwednesday_kmeans_tabular_2features-800.webp 800w,/assets%5Caio2025%5CM03W02%5Cwednesday_kmeans_tabular_2features-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W02%5Cwednesday_kmeans_tabular_2features.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 170px; max-height: 250px; " alt="the relationship of monthly expenditure to age" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">The relationship of monthly expenditure to age</figcaption> </figure> <strong>Exercise 3:</strong> Based on the code snippet for exercise 1 below and the following exercises, write code for exercise 2. <br/> <strong>Exercise 4:</strong> What is the output of the following? Explain your answer. <pre>
<code>
data = np.random.rand(64,2)
centroids = np.random.rand(5,2)
distances = (data - centroids)**2

print(data.shape)
print(centroids.shape)
print(distances.shape)
</code>
</pre> <pre>
<code>
data = np.random.rand(64,2)

data1 = data[:, np.newaxis, :]
data2 = data.reshape(1, 1, -1)

print(data1.shape)
print(data2.shape)
</code>
</pre> <pre>
<code>
data = np.random.rand(34,4)
centroids = np.random.rand(3,4)
distances = np.sqrt((data[:, np.newaxis, :] - centroids)**2)

print(data.shape)
print(centroids.shape)
print(distances.shape)
</code>
</pre> <strong>Exercise 5:</strong> Which one gives the solution? <pre>
<code>
# 32 point, 3 dimensions (3 features), 4 centroids
squared_norm = np.random.rand(32,3,4)
# what is the distance between points and centroids?
np.sqrt(squared_norm.sum(axis=0))
np.sqrt(squared_norm.sum(axis=1))
np.sqrt(squared_norm.sum(axis=2))
</code>
</pre> <pre>
<code>
# 32 point, 3 dimensions (3 features), 2 centroids
distances_detail = np.random.rand(32,3,2)
# what is the distance between points and centroids?
np.linalg.norm(distances_detail,axis=0)
np.linalg.norm(distances_detail,axis=1)
np.linalg.norm(distances_detail,axis=2)
</code>
</pre> </div> </details> <p><br/></p> <p>Below is the code snippet for the practice exercise 1 just above.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">numpy</span> <span class="k">as</span> <span class="n">np</span>

<span class="n">X</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">array</span><span class="p">([</span><span class="mf">1.4</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">1.5</span><span class="p">,</span> <span class="mf">3.1</span><span class="p">,</span> <span class="mf">3.8</span><span class="p">,</span> <span class="mf">4.1</span><span class="p">])</span>

<span class="c1"># step 1
</span><span class="n">k</span> <span class="o">=</span> <span class="mi">2</span>

<span class="c1"># step 2
</span><span class="n">centroids</span> <span class="o">=</span> <span class="n">X</span><span class="p">[:</span><span class="n">k</span><span class="p">]</span>

<span class="n">X</span> <span class="o">=</span> <span class="n">X</span><span class="p">.</span><span class="nf">reshape</span><span class="p">(</span><span class="mi">6</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span>

<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
    <span class="c1"># step 3a
</span>    <span class="n">distances</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">sqrt</span><span class="p">((</span><span class="n">X</span> <span class="o">-</span> <span class="n">centroids</span><span class="p">)</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">'</span><span class="s">distances:</span><span class="se">\n</span><span class="s"> </span><span class="si">{</span><span class="n">distances</span><span class="si">}</span><span class="sh">'</span><span class="p">)</span>

    <span class="c1"># step 3b
</span>    <span class="n">labels</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">argmin</span><span class="p">(</span><span class="n">D</span><span class="p">,</span> <span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">'</span><span class="s">labels:</span><span class="se">\n</span><span class="s"> </span><span class="si">{</span><span class="n">labels</span><span class="si">}</span><span class="sh">'</span><span class="p">)</span>

    <span class="c1"># step 4: update centroids
</span>    <span class="n">new_centroids</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">array</span><span class="p">([</span><span class="n">X</span><span class="p">[</span><span class="n">L</span> <span class="o">==</span> <span class="n">i</span><span class="p">].</span><span class="nf">mean</span><span class="p">()</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">k</span><span class="p">)])</span>

    <span class="c1"># step 5: check convergence
</span>    <span class="k">if</span> <span class="n">np</span><span class="p">.</span><span class="nf">all</span><span class="p">(</span><span class="n">centroids</span> <span class="o">==</span> <span class="n">new_centroids</span><span class="p">):</span>
        <span class="k">break</span>

    <span class="c1"># step 2: centroids
</span>    <span class="n">centroids</span> <span class="o">=</span> <span class="n">new_centroids</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">'</span><span class="s">centroids:</span><span class="se">\n</span><span class="s"> </span><span class="si">{</span><span class="n">centroids</span><span class="si">}</span><span class="sh">'</span><span class="p">)</span>
</code></pre></div></div> <p>We can also use an available package to solve the problem as well.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">sklearn.cluster</span> <span class="kn">import</span> <span class="n">KMeans</span>

<span class="n">kmeans</span> <span class="o">=</span> <span class="nc">KMeans</span><span class="p">(</span><span class="n">n_clusters</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">max_iter</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
<span class="n">kmeans</span><span class="p">.</span><span class="nf">fit</span><span class="p">(</span><span class="n">X</span><span class="p">.</span><span class="nf">reshape</span><span class="p">(</span><span class="mi">6</span><span class="p">,</span><span class="mi">1</span><span class="p">))</span>
<span class="n">labels</span> <span class="o">=</span> <span class="n">kmeans</span><span class="p">.</span><span class="n">labels_</span>
<span class="nf">print</span><span class="p">(</span><span class="n">labels</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="n">kmeans</span><span class="p">.</span><span class="n">inertia_</span><span class="p">)</span>

<span class="k">for</span> <span class="n">x</span><span class="p">,</span> <span class="n">label</span> <span class="ow">in</span> <span class="nf">zip</span><span class="p">(</span><span class="n">X</span><span class="p">,</span> <span class="n">labels</span><span class="p">):</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">'</span><span class="s">Cluster </span><span class="si">{</span><span class="n">label</span><span class="si">}</span><span class="s">: </span><span class="si">{</span><span class="n">x</span><span class="si">}</span><span class="sh">'</span><span class="p">)</span>
</code></pre></div></div> <p>In this example, our dataset consists of six data points $\{x_1, \cdots, x_6\}$ which are all one-dimensional real numbers, denoted $\mathbb{R^1}$ or $\mathbb{R}$. The initial $K = 2$ determines two clusters $\{S_0, S_1\}$.</p> <h3 id="formula-1"><strong>Formula</strong></h3> <p>Another <em>critical obstacle</em> when working with $K$-Means algorithm is to <em>obtain the optimal $K$ or the optimal number of clusters</em>. To do so, we need to determine how well the data points spread out from the centroids of their clusters.</p> <p>The <strong>Within-Cluster Sum of Squares (WCSS)</strong> works like a signal for the best $K$ (like in the Sample example); it measures how compact the clusters are using a clustering procedure, making it easier to compare different $K$-clustering results.</p> <p>The value of WCSS declines to 0 as $K$ grows. Using WCSS, we can figure out the optimal number of clusters via a method called the <strong>Elbow Method</strong>. This method aims to pick the <strong>elbow</strong> value where the rate of decrease for WCSS slows down sharply.</p> <div class="math-box"> $\text{Consider } \{x_1, \cdots, x_n\} \text{ where } x_i \in \mathbb{R}^d.$ $\text{Consider an initial set of K centroids } c_0, \cdots, c_K.$ $\text{Let } S = \{S_0, \cdots, S_K\} \text{ be K clusters.}$ $\text{WCSS is the sum of squared distances between each data point and the centroid of its assigned cluster and can be mathematically expressed as}$ $$\begin{align} WCSS = \sum_{i=0}^{k-1} \sum_{x \in S_i} \left\Vert x - c_i \right\Vert ^2 \end{align}$$ $\text{for } \left\Vert u \right\Vert _{L^2} ^2 = u_1^2 + \cdots + u_n^2.$ </div> <hr/> <h2 id="image-and-text-data"><strong>Image and Text Data</strong></h2> <p>We can apply $K$-Means to image and text datasets as well. Before doing so, we must preprocess the data so that it becomes pertain to the algorithm.</p> <h3 id="image-data"><strong>Image Data</strong></h3> <p>An image dataset can have many images. An image can be described as a matrix whose shape depends on its image type.</p> <p>To treat this kind of dataset as input of $K$-Means algorithm, we first need to make each image into a vector just like a row/data point in the tabular data above. That is, we flatten matrices.</p> <p>In practice, <em>Deep Learning</em> uses a <em>magic function</em> to process an image to extract its important parts which determine the content of the image. Just like our <em>human brain</em>, when we look at a cat or a dog image, we can tell what kind they are based on their face. The model completing this process is called a <em>pretrained model</em>. This reduces the size of data by a significant amount.</p> <h3 id="text-data"><strong>Text Data</strong></h3> <p>Similar to image data, there is a <em>magic function</em> to learn important words which speak the main content of an article.</p> <p>For text, we need to convert it into numeric values using vectorization algorithms before using $K$-Means.</p> <hr/> <h2 id="speech_balloon-faq">:speech_balloon: <strong>FAQ</strong></h2> <p>Here are some good questions asked during class. The answers to these questions came from either some admin, TA, STA, or members of AIO2025.</p> <table> <thead> <tr> <th>Questions</th> <th>Answers</th> </tr> </thead> <tbody> <tr> <td> </td> <td> </td> </tr> </tbody> </table> <p><br/></p> ]]></content><author><name></name></author><category term="AI"/><category term="coding"/><category term="AIO"/><category term="linear-algebra"/><summary type="html"><![CDATA[K-Means, unsupervised learning]]></summary></entry><entry><title type="html">Machine Learning 101 Warm-up</title><link href="https://huong-vo.github.io/blog/2025/KNN-pre101/" rel="alternate" type="text/html" title="Machine Learning 101 Warm-up"/><published>2025-08-08T00:00:00+00:00</published><updated>2025-08-08T00:00:00+00:00</updated><id>https://huong-vo.github.io/blog/2025/KNN-pre101</id><content type="html" xml:base="https://huong-vo.github.io/blog/2025/KNN-pre101/"><![CDATA[<p><strong>Acknowledgement:</strong> This post is based mainly on TA Duong Dinh Thang’s presentation and slides and partly on on TA, STA, and peers’ responses from the course AIO2025 offered by AI VIETNAM.</p> <p><strong>Relevance:</strong> The audience of this lecture were those who have read <a href="/blog/2025/data-visualization-analysis102/">Data Visualization and Analysis</a>, had some coding experience, and known a bit of AI knowledge.</p> <p>In today’s post, we’ll explore KNN and Supervised Learning. We’ll also discuss some applications in AI via a case study.</p> <hr/> <h2 id="machine-learning"><strong>Machine Learning</strong></h2> <h3 id="terminology"><strong>Terminology</strong></h3> <p><strong>Machine Learning (ML)</strong> is a subfield of <em>Artificial Intelligent (AI)</em> focused on the study of algorithms that can imitate how humans learn and ultilize data to generalize or predict unseen information, and thus gradually improving their performance on specific tasks without explicit programming.</p> <p>A familiar example is the classification problems we discussed in <strong>Math for AI series</strong>. Specifically, given a dataset with features and a class, we need to <em>foresee</em> a value of one column based on others. If a dataset is small enough, it may be possible to use multiple conditions to figure out. Nevertheless, it’s <em>hardly possible</em> for a large dataset with thousands and thousands lines of data. This is where ML comes into play.</p> <p>In particular, we feed an ML model the dataset whose features and class values are complete and accurate. Using the given condition, the model <em>takes note</em> and <em>learns</em> to find out which function can match the input and output together. Behind the scenes, this model repeatedly tries out every possible features-class pair. Once it’s done, we need to check how well the model is doing.</p> <p>The whole process includes two primary steps:</p> <ul> <li><strong>training</strong> for learning data, requiring <strong>training data</strong> which is the mentioned dataset.</li> <li><strong>testing</strong> for checking with non-dataset input, requiring <strong>test data</strong>.</li> </ul> <p>The ML model after learning the entire dataset is called a <strong>trained ML model</strong>. With the testing phase, we can conclude how well a trained ML model works. This is known as <strong>machine learning generalization</strong> which refers to a train ML model’s ability to perform well on unseen data of similar types. For example, a model classifying dogs and cats should not be pedantic and distinguish African dogs and Asian dogs; they are just dogs anyway. We’ll talk more about this in the future.</p> <h3 id="supervised-learning-sp"><strong>Supervised Learning (SP)</strong></h3> <p>Since this example of dataset has a <strong>class/label</strong>, we call it a <strong>labeled dataset</strong>. We call the type of machine learning where we use a labeled dataset to train a ML model a <strong>supervised learning (SL)</strong>. This name comes from the idea that the data is set with specific class values.</p> <p>A practical example is image classification for dogs and cats. The training data includes images of dogs and cats as a feature and a label column indicates which one is a cat and which one is a dog. The goal is for computers to answer if a test input image is a dog or a cat. Of course, if an input is a picture of a human being, the model will still result in a dog or a cat.</p> <p>SL can solve various problems which can be categorized into two types: <em>classification problems</em> and <em>regression problems</em>.</p> <p><strong>Classification Problems</strong> <br/> The class values in classification problems are discrete (categorical). Some examples can be dog-cat classification (0 and 1) and spam detection.</p> <p>In general, for classification problems, we want to find a border line that best divide a dataset into separate regions/classes.</p> <p><del>DEMONSTRATION</del></p> <p><strong>Regression Problems</strong> <br/> The output predicted in regression problems are continuous—any numerical value falling within a certain range. Some examples can be predicting salaries and forecasting stock prices.</p> <p>For regression problems, we generally want to find a line that best fits the data distribution.</p> <p><del>DEMONSTRATION</del></p> <hr/> <h2 id="k-nearest-neighbors-knn"><strong>$k$-Nearest Neighbors (KNN)</strong></h2> <p>The <em>core idea</em> of KNN comes straight from the saying, “Show me your friends and I’ll tell you who you are.” That is, if you know someone’s circle, you’ll know what that person is like.</p> <p>Formally, <strong>KNN</strong> is a <em>non-parametric</em> and SL algorithm which is used to make predictions based on proximity. In particular, the predicted class depends on the majority label / average value of its k-closest neighbors in the same feature space. This algorithm can be used for both <em>classification problems</em> and <em>regression problems</em>.</p> <p>Some common <strong>metrics</strong> used to determine k-closet neighbors are listed in the table below. In the following formulae, $n$ is the number of features and $x_i$ and $y_i$ are coordinates of training and test points.</p> <table class="language-python"> <tr> <th>Metric</th> <th>Formula</th> </tr> <tr> <th scope="row">Chebyshev distance&lt;/strong&gt;</th> <td>$$d = \lim_{p \to \infty} \left(\sum_{i=1}^n \|x_i - y_{i} \| ^p \right)^\frac{1}{p}$$</td> </tr> <tr> <th scope="row">Euclidean distance&lt;/strong&gt;</th> <td>$$d = \left(\sum_{i=1}^n \|x_i - y_{i} \| ^2 \right)^\frac{1}{2}$$</td> </tr> <tr> <th scope="row">Manhattan distance&lt;/strong&gt;</th> <td>$$d = \sum_{i=1}^n \|x_i - y_{i} \|$$</td> </tr> <tr> <th scope="row">Minkowski distance&lt;/strong&gt;</th> <td>$$d = \left(\sum_{i=1}^n \|x_i - y_{i} \| ^4 \right)^\frac{1}{4}$$</td> </tr> </table> <p><br/></p> <p><del>DEMONSTRATION for distances</del></p> <h3 id="procedure"><strong>Procedure</strong></h3> <p>We should always preprocess the data first. Then we can apply it to a ML model in the following steps.</p> <ol> <li><em>Initialize</em> the value of $k$.</li> <li><em>Compute the distance</em> between test data and every training data (every row of the training dataset).</li> <li><em>Sort</em> the calculated distances in ascending order.</li> <li><em>Get top $k$ elements</em> from the sorted array.</li> <li><em>Get the most frequent class</em> of these elements.</li> <li><em>Return</em> the predicted class.</li> </ol> <pre><code class="language-mermaid">flowchart LR
    A((&lt;strong&gt;Initialize k&lt;/strong&gt;)) ---&gt; B((&lt;strong&gt;Compute distances&lt;/strong&gt;&lt;/br&gt; between test data&lt;/br&gt; and training data)) ---&gt; C((&lt;strong&gt;Sort distances&lt;/strong&gt;&lt;/br&gt; in ascending order)) ---&gt; D((&lt;strong&gt;Get top k elements&lt;/strong&gt;&lt;/br&gt; from the sorted array)) ---&gt; E((&lt;strong&gt;Get the most frequent class&lt;/strong&gt;&lt;/br&gt; of these elements)) ---&gt; F((&lt;strong&gt;Return predicted class&lt;/strong&gt;))
</code></pre> <figcaption style="text-align: center; width: 100%; font-size: 0.875em; margin-top:-5px; margin-bottom:15px;"> <em>Figure .</em> KNN Procedure </figcaption> <p>When working on step 3, keep in mind that we want to know the training points rather than just the distances. That is, we need to obtain the <em>indices</em> of training data.</p> <p>The steps from 1 through 4 are quite straightforward while performing step 5 takes a bit more effort. This happens because of their class type differences. For classification problems, class values are discrete wheareas they are continuous for regression problems. That is, we need to approach two tasks via different strategies:</p> <ul> <li> <p><strong>Classification problems:</strong> discrete class set allows us to vote for the <em>majority</em> label from the top $k$.</p> \[\hat{y} = max \\\{y_x | x \in NB \\\}\] </li> <li> <p><strong>Regression problems:</strong> continuous class set requires us to <em>average</em> the $k$-neighbors’ values.</p> \[\hat{y} = \frac{1}{k} \sum_{x \in NB} y_x\] </li> </ul> <p>:pushpin: <strong>Note:</strong></p> <ul> <li>Results are potentially different depending on the initialization of $k$. In Machine Learning, parameters like k are referred to as <strong>hyperparameters</strong>—predictions change as hyperparameters change.</li> <li>If the number of labels equal each other, there are many ways to go about it. We can either increase $k$ or assign the label of the closet one.</li> </ul> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> Use the following table and step-by-step procedure with Euclidean distance to check which label the test point $x\_test = 2.4$ should be when <ol> <li> $k = 1$ </li> <li> $k = 2$ </li> <li> $k = 3$ </li> <li> $k = 4$ </li> <li> $k = 5$ </li> </ol> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_1feature-480.webp 480w,/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_1feature-800.webp 800w,/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_1feature-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_1feature.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 300px; max-height: 300px; " alt="knn procedure exercise 1" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption"></figcaption> </figure> <strong>Exercise 2:</strong> Do the same as exercise 1 for $x\_test = (2.4, 0.8)$ and $x\_test = (2.6, 0.8)$ when <ol> <li> $k = 1$ </li> <li> $k = 3$ </li> <li> $k = 5$ </li> </ol> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features-480.webp 480w,/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features-800.webp 800w,/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 300px; max-height: 300px; " alt="knn procedure exercise 2" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption"></figcaption> </figure> <strong>Exercise 3:</strong> Write code for both exercises above. <br/> <strong>Exercise 4:</strong> Use the following table and step-by-step procedure with Euclidean distance to predict the salary for $experience = 5.3$ when <ol> <li> $k = 2$ </li> <li> $k = 3$ </li> <li> $k = 4$ </li> </ol> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features-480.webp 480w,/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features-800.webp 800w,/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 300px; max-height: 300px; " alt="knn procedure exercise 2" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption"></figcaption> </figure> <strong>Exercise 5:</strong> Write code for exercise 4 above. </div> </details> <p><br/></p> <hr/> <h2 id="scaling"><strong>Scaling</strong></h2> <p>As we’ve known so far, <em>features</em> and <em>distances</em> are the main contributors of KNN algorithm. In practice, if there is a problem with feature values, the distances will cause issues.</p> <p>For example, if values of feature A are way greater than that of other features, the feature A can influence and dominate the results while it may not reflect its actual meaning/importance. This results in the <em>incorrect learning of ML models</em>—learning based on values solely rather than relevant meaning. It happens when we apply the procedure without any extra consideration.</p> <figure> <div style="display: flex; flex-wrap:wrap; column-gap: 20px; justify-content: center"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features-480.webp 480w,/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features-800.webp 800w,/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W02%5Ctuesday_knn_exercise_procedure_2features.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 300px; max-height: 300px; " alt="petal length values are much greater than petal width, increasing the distances along with them, so the distances increase according to and are determined by length" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">Petal length values are much greater than petal width. So, the distances increase according to and are determined more by length.</figcaption> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W02%5Ctuesday_knn_procedure_issue-480.webp 480w,/assets%5Caio2025%5CM03W02%5Ctuesday_knn_procedure_issue-800.webp 800w,/assets%5Caio2025%5CM03W02%5Ctuesday_knn_procedure_issue-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W02%5Ctuesday_knn_procedure_issue.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 320px; max-height: 450px; " alt="etal length and petal width have different units" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">Petal length and petal width have different units</figcaption> </figure> </div> <figcaption style="text-align: center; width: 100%; font-size:0.875em; margin-top:-25px;"> <em>Figure 1.</em> Examples of issue without scaling </figcaption> </figure> <p>To resolve this issue, we can build an algorithm that explicitly tells the model to focus on more significant features. Nevertheless, we do <em>not</em> want to make such suggestions every single time we work on a problem, which is quite a waste of resources. This is where <em>scaling</em> comes in handy.</p> <p><strong>Scaling</strong> is a crucial step in data preprocessing that normalizes and standardizes feature values (both training and test points) to fit in a certain domain. This helps the KNN algorithm focus on <em>feature importance</em>, ensuring feature values’ <em>fair contributions</em> to <em>distance-based</em> or <em>gradiant-sensitive</em> algorithm.</p> <p>Choosing a domain depends on which scaling algorithm we use. Common domains can be $(0, 1)$ and $(-1, 1)$. One of the formula for normalization is the following</p> \[new\_x_i = \frac{x_i - \bar{x}}{\sigma}\] <p>where $\bar{x}$ is the mean and $\sigma$ is the standard deviation of training data.</p> <p>In practice, we use packages for scaling. One that is based on the above formula is</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># import a package
</span><span class="kn">from</span> <span class="n">sklearn.preprocessing</span> <span class="kn">import</span> <span class="n">StandardScaler</span>

<span class="c1"># call the function
</span><span class="n">scaler</span> <span class="o">=</span> <span class="nc">StandardScaler</span><span class="p">()</span>

<span class="c1"># scale training and test values
</span><span class="n">x_data_scaled</span> <span class="o">=</span> <span class="n">scaler</span><span class="p">.</span><span class="nf">fit_transform</span><span class="p">(</span><span class="n">x_data</span><span class="p">)</span>
<span class="n">x_test_scaled</span> <span class="o">=</span> <span class="n">scaler</span><span class="p">.</span><span class="nf">transform</span><span class="p">(</span><span class="n">x_test</span><span class="p">)</span>
</code></pre></div></div> <h2 id="knn-for-classification-tasks"><strong>KNN for Classification Tasks</strong></h2> <details class="bordered-block"> <summary> <strong>Text Classification with KNN</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Problem statement:</strong> Solve the task of text classification using KNN with the given procedure. <br/> <strong>Breakdown:</strong> <ul> <li> Input is a corpus of texts or strings and a random string that is not in the corpus. </li> <li> Output is a corpus string that is potentially closet to the random string. </li> </ul> <strong>Idea:</strong> We follow the procedure of the KNN algorithm. <ol> <li> To build a model, we follow a <strong>pipeline</strong> which is a complete model building process. </li> <ol> <li> <em>Obtain message input</em> with its label values. </li> <li> <em>Preprocessing: convert</em> message to numeric values like vectors. </li> <li> <em>Build vocabulary</em>. </li> <li> <em>Create features</em>. </li> <li> <em>Pass to a KNN model</em>. </li> <li> <em>Obtain results</em>. </li> </ol> <li> We can handle preprocessing to build vocabulary via two approaches: <strong>Bag-of-Words (BoW)</strong> and <strong>Term Frequency-Inverse Document Frequency (TF-IDF)</strong>. </li> </ol> :pushpin: <strong>Note:</strong> Essentially, the KNN algorithm stays similar for all types of data: image, sound, tabular, and time series data. But we need to figure out how to work with given data to induce real feature values. </div> </details> <p><br/> <em>BoW</em> is simpler and more basic approach to text representation than <em>TF-IDF</em>. BoW only considers word occurences while TF-IDF provides more <em>informative</em> representation of text—taking into account both word importance and context within the entire dataset. <em>TF-IDF is generally preferred</em>. We’ll delve deeper into the two techniques right away.</p> <details class="bordered-block"> <summary> <strong>Bag-of-Words (BoW)</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Problem statement:</strong> We have the following corpus. <table class="language-python"> <tr> <th>Doc</th> <th>Label</th> </tr> <tr> <td>you reap what you sow</td> <td class="centered_td">1</td> </tr> <tr> <td>beggars can't be choosers</td> <td class="centered_td">1</td> </tr> <tr> <td>like bees to honey</td> <td class="centered_td">1</td> </tr> <tr> <td>bite the hands that feed you</td> <td class="centered_td">0</td> </tr> <tr> <td>a taste of your own medicine</td> <td class="centered_td">0</td> </tr> <tr> <td>burn your bridges behind you</td> <td class="centered_td">0</td> </tr> </table> <br/> <strong>Idea:</strong> BoW is to investigate distances between two sentences based on word occurences. <ol> <li> <strong>Tokenization</strong> splits text into a list of smaller parts or separate words. </li> <li> Initialize an empty vocabulary. Add new words from tokenized corpus into the vocabulary. </li> Vocabulary = <li> Take a random string: <em>"you can't pick and choose"</em>. </li> <li> Convert the string to a numeric value by creating a <strong>BoW</strong> from the string and assigning a number to each of them. Two common ways are </li> <ul> <li> <strong>Binary BoW:</strong> Words in the string are assigned $1$ and others are $0$. </li> <li> <strong>(Complete) BoW:</strong> Each value is the number of its' word <em>occurrency</em>. </li> </ul> We use the <em>(complete) BoW</em> for this problem. <ul> <li> From here, we build a matrix where each row is a word in the constructed vocabulary, each column is a sentence from the corpus, and each cell of a column is the BoW value of the corresponding sentence. Corpus sentences now become <em>training vectors/points</em>. </li> <li> With the random string, we can build a <em>test vector/point</em>. </li> </ul> <s>DEMONSTRATION for matrix</s> </ol> <br/> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Cwednesday_1Dinterpolation_nearest_neighbor-480.webp 480w,/assets%5Caio2025%5CM03W01%5Cwednesday_1Dinterpolation_nearest_neighbor-800.webp 800w,/assets%5Caio2025%5CM03W01%5Cwednesday_1Dinterpolation_nearest_neighbor-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Cwednesday_1Dinterpolation_nearest_neighbor.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 320px; max-height: 450px; " alt="" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption"></figcaption> </figure> </div> </details> <details class="bordered-block"> <summary> <strong>Term Frequency-Inverse Document Frequency (TF-IDF)</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Problem statement:</strong> We use the same corpus as in BoW. We can also use this approach to do spam email detection. <br/> <strong>Breakdown:</strong> <ul> <li> A document is a sentence/ an email. </li> <li> The corpus is a collection of documents. </li> </ul> <strong>Idea:</strong> TF-IDF is to compute the importance level of a word or phrase relative to its parent sentence/email and the entire corpus. Thus, TF-IDF helps identify words that are significant in a specific document but maybe rare across all the documents. <ol> <li> We build the same matrix for the entire corpus as in Bag-of-Words technique. </li> <li> We build three other matrices: </li> <ul> <li> <strong>TF matrix:</strong> Each cell is the corresponding TF value of the row word and the column document. </li> <li> <strong>IDF matrix:</strong> Each cell is the corresponding IDF value of the row word and the column document. </li> <li> <strong>TF-IDF matrix:</strong> Each cell is the corresponding TF-IDF value of the row word and the column document. </li> <li> <strong>Normalized TF-IDF matrix:</strong> Each cell is the corresponding normalized TF-IDF value of the row word and the column document. </li> </ul> <li> We build vectors for test documents. </li> <li> <strong>Term Frequency (TF)</strong> measures how often a term/word appears in a document, that is how important a word is in a document. </li> $$\begin{align*} TF_{(t,d)} &amp;= count(t,d) \\ &amp;= \frac{\text{Number of times word$_\textbf{i}$ appears in a document}}{\text{Total number of words in the document}} \end{align*}$$ or <em>in practice</em>, $$TF_{(t,d)} = \log{(count(t,d) + 1)}$$ However, a word with a high occurence may not actually be of large importance, for example "the", "and", "are", and "is". We need additional step to handle this situation. <li> <strong>Inverse Document Frequency (IDF)</strong> measures how important a word is across the entire corpus. </li> $$\begin{align*} IDF_{t} &amp;= \log \frac{\text{Total number of documents}}{\text{Number of documents containing word$_\textbf{i}$}} \\ &amp;= \frac{N}{DF_t} \end{align*}$$ or <em>in practice</em>, $$\begin{align*} IDF_{t} &amp;= \log \frac{\text{Total number of documents + 1}}{\text{Number of documents containing word$_\textbf{i}$ + 1}} + 1 \\ &amp;= \frac{N + 1}{DF_t + 1} + 1 \end{align*}$$ Adding $1$ is to avoid $0$ as a result since it could kill of the importance of a word and avoid division by zero. This technique is called <strong>smoothing</strong> used in packages like <strong>scikit-learn</strong>. <li> <strong>TF-IDF</strong> computes the importance of a word in an email relative to the entire collection. </li> $$TF-IDF_{(t,d)} = TF_{(t,d)} \times IDF_{t}$$ This multiplication helps compensate for the disproportionate influence of frequently occuring but uninformative words and ensure the more meaningful features contribute appropriately to the distance calculations. <br/> This next step is not required. The TF-IDF matrix contains many zeros and possiblly much greater values, so we need to normalize them like we perform scaling in KNN. This helps avoid bias from document length. <li> <strong>Normalized TF-IDF</strong> takes the unit vector of each TF-IDF vector. <strong>L2 norm</strong> is also known as <strong>Euclidean norm</strong>. </li> $$L2\_norm(TF-IDF) = \frac{TF-IDF}{\left \| TF-IDF \right \|_2} = \frac{\langle x_1, \cdots, x_n}{\left( \sum_i=1^n x_i^2\right)^\frac{1}{2}}$$ <s>DEMONSTRATION for matrix</s> </ol> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Cwednesday_1Dinterpolation_nearest_neighbor-480.webp 480w,/assets%5Caio2025%5CM03W01%5Cwednesday_1Dinterpolation_nearest_neighbor-800.webp 800w,/assets%5Caio2025%5CM03W01%5Cwednesday_1Dinterpolation_nearest_neighbor-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Cwednesday_1Dinterpolation_nearest_neighbor.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 320px; max-height: 450px; " alt="" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption"></figcaption> </figure> </div> </details> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> Given a corpus of two documents: doc 1 = "apple banana" and doc 2 = "banana banana". Compute TF, IDF, and TF-IDF (both theoretical and practical). <br/> <strong>Exercise 2:</strong> Write code to complete the example for each approach. </div> </details> <hr/> <h2 id="case-study-code-implementation"><strong>Case Study: Code Implementation</strong></h2> <p>We primarily use the <strong>scikit-learn</strong> library for ML problems.</p> <p><strong>Problem statement:</strong> <em>Build a system to predict whether a tumor is maglinant or benign based on real-valued information using KNN.</em></p> <p><strong>Set Up</strong></p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># for a tabular dataset
</span><span class="kn">import</span> <span class="n">pandas</span> <span class="k">as</span> <span class="n">pd</span>

<span class="c1"># this dataset is available in the package, so we just need to import it
</span><span class="kn">from</span> <span class="n">sklearn.datasets</span> <span class="kn">import</span> <span class="n">load_breast_cancer</span>

<span class="c1"># 
</span><span class="kn">from</span> <span class="n">sklearn.model_selection</span> <span class="kn">import</span> <span class="n">train_test_split</span>

<span class="c1"># for scaling data (mentioned above)
</span><span class="kn">from</span> <span class="n">sklearn.preprocessing</span> <span class="kn">import</span> <span class="n">StandardScaler</span>

<span class="c1"># KNN algorithm in the package
</span><span class="kn">from</span> <span class="n">sklearn.neighbors</span> <span class="kn">import</span> <span class="n">KNeighborsClassifier</span>

<span class="c1"># for classification tasks and assessing how accurate our model is
</span><span class="kn">from</span> <span class="n">sklearn.metrics</span> <span class="kn">import</span> <span class="n">accuracy_score</span><span class="p">,</span> <span class="n">classification_report</span>
</code></pre></div></div> <p>The <strong>pipeline</strong> for a ML problem follows the basic procedure below. In practice, there can more steps to work through.</p> <ol> <li>Data input</li> <li>Data preprocessing</li> <li>Data spliting: <ul> <li>Training data</li> <li>Test data</li> </ul> </li> <li>Data training</li> <li>Model evaluation</li> </ol> <p><strong>Data Input</strong></p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># load and read the dataset
</span><span class="n">cancer</span> <span class="o">=</span> <span class="nf">load_breast_cancer</span><span class="p">()</span>

<span class="c1"># convert the feature columns to a DataFrame
</span><span class="n">X</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nc">DataFrame</span><span class="p">(</span><span class="n">cancer</span><span class="p">.</span><span class="n">data</span><span class="p">,</span> <span class="n">columns</span><span class="o">=</span><span class="n">cancer</span><span class="p">.</span><span class="n">feature_names</span><span class="p">)</span>

<span class="c1"># convert the label column to a Series
</span><span class="n">y</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nc">Series</span><span class="p">(</span><span class="n">cancer</span><span class="p">.</span><span class="n">target</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="sh">'</span><span class="s">target</span><span class="sh">'</span><span class="p">)</span>
</code></pre></div></div> <p><del>DEMONSTRATION FOR FIRST FIVE ROWS</del></p> <p>Since the target contains non-numerical values: benign and malignant, we need to assign numeric values to them: $1$ for benign and $0$ for malignant.</p> <p><strong>Data Spliting</strong> <br/> We mentioned splitting a dataset (label included) into two sets: training and test data. But in practice, a dataset is usally divided into three following sets:</p> <ul> <li>Training data</li> <li>Test data</li> <li><em>Evaluation data:</em> we use this set to evaluate how accurate the ML model is—checking how close its predicted label values is to its actual label values, then check the same for test data.</li> </ul> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">val_size</span> <span class="o">=</span> <span class="mf">0.2</span>
<span class="n">test_size</span> <span class="o">=</span> <span class="mf">0.125</span>
<span class="n">random_state</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">is_shuffle</span> <span class="o">=</span> <span class="bp">True</span>

<span class="c1"># split training and test data
</span><span class="n">X_train</span><span class="p">,</span> <span class="n">X_test</span><span class="p">,</span> <span class="n">y_train</span><span class="p">,</span> <span class="n">y_test</span> <span class="o">=</span> <span class="nf">train_test_split</span><span class="p">(</span>
    <span class="n">X</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> 
    <span class="n">test_size</span><span class="o">=</span><span class="n">test_size</span><span class="p">,</span> 
    <span class="n">shuffle</span><span class="o">=</span><span class="n">is_shuffle</span><span class="p">,</span>
    <span class="n">random_state</span><span class="o">=</span><span class="n">random_state</span><span class="p">)</span>

<span class="c1"># split training and evaluation data
</span><span class="n">X_train</span><span class="p">,</span> <span class="n">X_val</span><span class="p">,</span> <span class="n">y_train</span><span class="p">,</span> <span class="n">y_val</span> <span class="o">=</span> <span class="nf">train_test_split</span><span class="p">(</span>
    <span class="n">X</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> 
    <span class="n">test_size</span><span class="o">=</span><span class="n">val_size</span><span class="p">,</span> 
    <span class="n">shuffle</span><span class="o">=</span><span class="n">is_shuffle</span><span class="p">,</span>
    <span class="n">random_state</span><span class="o">=</span><span class="n">random_state</span><span class="p">)</span>
</code></pre></div></div> <p><strong>Data Training</strong> <br/></p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># scale data
# call scaling function
</span><span class="n">scaler</span> <span class="o">=</span> <span class="nc">StandardScaler</span><span class="p">()</span>

<span class="c1"># scale both training and test data
</span><span class="n">X_train_scaled</span> <span class="o">=</span> <span class="n">scaler</span><span class="p">.</span><span class="nf">fit_transform</span><span class="p">(</span><span class="n">X_train</span><span class="p">)</span>
<span class="n">X_test_scaled</span> <span class="o">=</span> <span class="n">scaler</span><span class="p">.</span><span class="nf">transform</span><span class="p">(</span><span class="n">X_test</span><span class="p">)</span>
</code></pre></div></div> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># initialize k
</span><span class="n">k</span> <span class="o">=</span> <span class="mi">5</span>

<span class="c1"># call KNN algorithm
</span><span class="n">knn_classifier</span> <span class="o">=</span> <span class="nc">KNeighborsClassifier</span><span class="p">(</span><span class="n">n_neighbors</span><span class="o">=</span><span class="n">k</span><span class="p">)</span>

<span class="c1"># train data
</span><span class="n">knn_classifier</span><span class="p">.</span><span class="nf">fit</span><span class="p">(</span><span class="n">X_train_scaled</span><span class="p">,</span> <span class="n">y_train</span><span class="p">)</span>
</code></pre></div></div> <p><del>DEMONSTRATION IN SLIDE, ONLY TAKE 2 FOR VISUALIZATION, SHOULD BE 30 BUT THERE IS NO WAY TO VISUALIZE 30 DIMENSIONS</del></p> <p><strong>Model Evaluation</strong> <br/> This step plays a very important role during the model development. Depending on the result, we can tune parameters, try different metrics, process the data differently or other methods. Once we get high-enough accuracy, we can start testing on test data.</p> <p>You can think of evaluation data as a practice exam while test data is a real exam.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># make evaluation on evaluation data
</span>
<span class="c1"># test on evaluation data
</span><span class="n">y_pred</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="nf">predict</span><span class="p">(</span><span class="n">X_val_scaled</span><span class="p">)</span>

<span class="c1"># check the model's accuracy on evaluation data
</span><span class="n">accuracy</span> <span class="o">=</span> <span class="nf">accuracy_score</span><span class="p">(</span><span class="n">y_val</span><span class="p">,</span> <span class="n">y_pred</span><span class="p">)</span>

<span class="c1"># obtain precision, recall, and f1-score values
</span><span class="n">report</span> <span class="o">=</span> <span class="nf">classification_report</span><span class="p">(</span><span class="n">y_val</span><span class="p">,</span> <span class="n">y_pred</span><span class="p">)</span>
</code></pre></div></div> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># make evaluation on test data
</span>
<span class="c1"># test on test data
</span><span class="n">y_pred</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="nf">predict</span><span class="p">(</span><span class="n">X_test_scaled</span><span class="p">)</span>

<span class="c1"># check the model's accuracy on test data
</span><span class="n">accuracy</span> <span class="o">=</span> <span class="nf">accuracy_score</span><span class="p">(</span><span class="n">y_test</span><span class="p">,</span> <span class="n">y_pred</span><span class="p">)</span>

<span class="c1"># obtain precision, recall, and f1-score values
</span><span class="n">report</span> <span class="o">=</span> <span class="nf">classification_report</span><span class="p">(</span><span class="n">y_test</span><span class="p">,</span> <span class="n">y_pred</span><span class="p">)</span>
</code></pre></div></div> <p>We can write our own code for checking the accuracy above using the formula</p> \[Accuracy = \frac{\text{Numer of correct predictions}}{\text{Total numebr of predictions}}.\] <p>The same goes for <em>precision</em>, <em>recall</em>, and <em>F1-Score</em> values.</p> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> Write code to display the first five rows, data information, statistics information, numeric value assignments, <br/> <strong>Exercise 2:</strong> What are the accuracy scores for evaluation and test data? </div> </details> <hr/> <h2 id="knn-with-k-d-tree"><strong>KNN with K-D Tree</strong></h2> <p>The original KNN algorithm has its own drawback, so methods like K-D Tree are introduced to improve the algorithm’s performance.</p> <hr/> <h2 id="speech_balloon-faq">:speech_balloon: <strong>FAQ</strong></h2> <p>Here are some good questions asked during class. The answers to these questions came from either some admin, TA, STA, or members of AIO2025.</p> <table> <thead> <tr> <th>Questions</th> <th>Answers</th> </tr> </thead> <tbody> <tr> <td>Can the idea of calculating distances from a test point to training points be applied to a very large dataset? It seems to cost a lot of time. Are there any ways tp resolve this issue?</td> <td>There are improved KNN like KD-tree developed to reduce algorithm complexity. Or we can use <strong>Aritificial Neural Networks (ANN)</strong> algorithm. One of the methods is to divide it into smaller sets and check which set a test point belongs to. <strong>Facebook AI Similarity Search (FAISS)</strong> can do this.</td> </tr> <tr> <td>What do I do when images are input?</td> <td>Input images can be converted into a table of multiple features. We then have more complex algorithms to handle them.</td> </tr> <tr> <td>Can classification problems be applied for more than two label values?</td> <td>Yes.</td> </tr> <tr> <td>When training data with a model is used in practice, does this process need repeating? Does it depend on the data size?</td> <td>Yes, how we repeat it depends on specific problems. There are two types of models: pre-trained model—training process happens in advanced using a fixed dataset then is applied to new data and online training model—continuous update occurs in real time as new data arrives.</td> </tr> <tr> <td>If the dataset is image, how can we classify?</td> <td>Answer</td> </tr> <tr> <td>Can KNN be used for many features?</td> <td>Yes. Performance may be degraded since data becomes sparse/far apart in a high-dimensional space as the space gets bigger, that is, distance measures becomes less meaningful. This is called <strong>curse of dimensionality</strong>.</td> </tr> <tr> <td>How do I split a dataset into three sets?</td> <td>You can use <code class="language-plaintext highlighter-rouge">GridSearchCV</code> to check out most effective ratios.</td> </tr> </tbody> </table> <p><br/></p> ]]></content><author><name></name></author><category term="AI"/><category term="coding"/><category term="AIO"/><category term="basic-math"/><summary type="html"><![CDATA[KNN, supervised learning, classification, regression]]></summary></entry><entry><title type="html">Data Visualization and Analysis for AI 102</title><link href="https://huong-vo.github.io/blog/2025/data-visualization-analysis102/" rel="alternate" type="text/html" title="Data Visualization and Analysis for AI 102"/><published>2025-08-06T00:00:00+00:00</published><updated>2025-08-06T00:00:00+00:00</updated><id>https://huong-vo.github.io/blog/2025/data-visualization-analysis102</id><content type="html" xml:base="https://huong-vo.github.io/blog/2025/data-visualization-analysis102/"><![CDATA[<p><strong>Acknowledgement:</strong> This post is based mainly on Vinh Dinh Nguyen’s presentation and slides and partly on on TA, STA, and peers’ responses from the course AIO2025 offered by AI VIETNAM.</p> <p><strong>Relevance:</strong> The audience of this lecture were those who have read <a href="/blog/2025/data-visualization-analysis101/">Data Visualization and Analysis for AI 101</a>, had some coding experience, and known a bit of AI knowledge.</p> <p>Visualizing data <em>properly</em> plays a significant role in learning how data is distributed and related before we delve into complex data analysis. In today’s post, we’ll explore common types of charts used in data visualization and more importantly, how to choose the right one for our dataset in general. We’ll look at real-world examples and case studies to see how different chart types work in action, giving more hands-on feel for the process.</p> <hr/> <h2 id="data-visualization"><strong>Data Visualization</strong></h2> <p><strong>Data visualization</strong> is the process of transforming image, number, sound, or text information into visual elements like maps or graphs. In this way, we, as human users, can approach, comprehend, and identify <em>insights</em> from the information as a whole better and more easily.</p> <p>If we have thousands and thousands of lines of values, it’d be extremely challenging to look at them and draw a conclusion. A chart made out of such a dataset can be much easier on the eye, not only for data experts but also regular people. For example, we wouldn’t know how president candidates are doing nationwide by just looking at numbers because of how Electoral College works.</p> <p>Data visualization can be used on a wide range of data and take on various forms including 2Ds and 3Ds. Good data visualization helps lay the groundwork for further analysis like data mining, which ultimately supports smarter decision-making. There are different packages that we can use to visualize a dataset. In this post, we’ll focus on using three main ones:</p> <ul> <li><strong>matplotlib:</strong> it is a quite complete visualization tool, offering extensive customization but requires a lot of parameters passed to a function, so users need to understand them more clearly. <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>
</code></pre></div> </div> </li> <li><strong>seaborn:</strong> it simplifies statiscal plots with built-in themes, focusing more on data itself rather than its meaning but makes it harder to customize the output. <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">seaborn</span> <span class="k">as</span> <span class="n">sns</span>
</code></pre></div> </div> </li> <li><strong>plotly:</strong> it provides dynamic and interactive visualization which users can engage with charts and see details without complicating charts. <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">plotly.express</span> <span class="k">as</span> <span class="n">px</span>
</code></pre></div> </div> </li> </ul> <p>It takes skill and experience to create a effective visualization. At its core, it all starts with the fundamental process. The figure below displays the full data science cycle—how visualization fits into the bigger picture.</p> <pre><code class="language-mermaid">flowchart LR
    A((Ask a question)) ---&gt; B((Gather the data)) ---&gt; C((Analyze the data)) ---&gt; D((Convey the data)) ---&gt; E((Communicate&lt;br/&gt;and Visualize&lt;br/&gt;the results))
    E ---&gt; D ---&gt; C ---&gt; B ---&gt; A
    E ----&gt; A
</code></pre> <figcaption style="text-align: center; width: 100%; font-size: 0.875em; margin-top:-5px; margin-bottom:15px;"> <em>Figure 1.</em> Data Science Process </figcaption> <p>:pushpin: <strong>Pro tip:</strong> Learning how to use libraries to visualize datasets is a small step in the big picture. Beginners should focus on building and sharpening critical thinking and analysis mindset to understand data and determine what charts to apply and how to use them.</p> <hr/> <h2 id="choose-the-right-chart-type"><strong>Choose the Right Chart Type</strong></h2> <p>There are <em>five fundamental steps</em> to go through when it comes to choosing the right chart type. Practically, we need to modify this process to be suitable for our datasets.</p> <h3 id="determine-the-type-of-data"><strong>Determine the Type of Data</strong></h3> <p>Data can be categorized into four following types with the first three being commonly encountered.</p> <ul> <li><strong>Quantitative data:</strong> numerical values.</li> <li><strong>Categorical data:</strong> non-numerical values, for example, text.</li> <li><strong>Temporal data:</strong> numerical and time-series values.</li> <li><strong>Spatial data:</strong> location-based values.</li> </ul> <h3 id="identify-the-relationship-between-variables"><strong>Identify the Relationship between Variables</strong></h3> <p>A dataset can have hundreds of variables/properties which are stored in columns. When working with a dataset, we need to understand its properties and know which ones are valuable to problems raised. That is, we have to learn how they are linked with each other within the dataset. As such, we can narrow down the kinds of charts we should use to magnify their relationship.</p> <p>There are four primary types of charts that corresponds to connection between variables.</p> <ul> <li><strong>Comparison chart</strong> <ul> <li>used for showing differences <ul> <li>among multiple data</li> <li>over time (to indicate trend)</li> </ul> </li> <li>a bar chart, column, or line chart.</li> </ul> </li> <li><strong>Distribution chart</strong> <ul> <li>used for showing how data is dispersed</li> <li>a histogram or box plot.</li> </ul> </li> <li><strong>Relationship chart</strong> <ul> <li>used for showing how multiple properties are related</li> <li>a scatter plot or bubble chart.</li> </ul> </li> <li><strong>Composition chart</strong>.</li> </ul> <h3 id="determine-the-purpose-of-visualization"><strong>Determine the Purpose of Visualization</strong></h3> <p>This is where asking questions also comes into play. This step can help us indicate what we really want from a dataset and what message we want to deliver through visualization.</p> <p>Eventually, we want to answer the question how and what we want to show our data visualization.</p> <ul> <li>if we want to <em>compare</em> data points, a bar or column chart may be appropriate.</li> <li>if we want to show the <em>distribution</em> of data in specific properties a histogram or box plot might be useful.</li> <li>if we want to show a <em>trend</em> over time, we can use a line or area chart.</li> </ul> <h3 id="identify-the-audience"><strong>Identify the Audience</strong></h3> <p>Just like public speaking, <em>knowing</em> our audiences—who we are creating charts for—is key when it comes to data visualization. The answer should guide how complex or simple our visuals need to be.</p> <ul> <li>For experts in the field, more advanced visuals like heat maps can be an adequate choice since these charts are equipped to interpret the details.</li> <li>For clients with little to none techincal background, keeping things simple like pie or bar charts is often more effective.</li> </ul> <h3 id="select-the-appropriate-chart-type"><strong>Select the Appropriate Chart Type</strong></h3> <p>There’s <em>no</em> such thing as a one-size-fits-all solution to a data-related problem. Different probelms can have different visual solutions; it sometimes takes more than one chart to tell the full story clearly.</p> <p>When we’re getting our feet wet, it’s imperative that we experiment various chart types. Play around, compare, and see which ones best present our data. Over time, as we become more experienced with different types of data and visuals, choosing the right chart will start to feel more intuitive and much quicker.</p> <hr/> <h2 id="case-study-1-electricity-transformer-dataset-etd"><strong>Case Study 1: <a href="https://github.com/zhouhaoyi/ETDataset">Electricity Transformer Dataset (ETD)</a></strong></h2> <p>Some abbreviations used in this dataset are in the following table.</p> <table> <thead> <tr> <th style="text-align: center">Field</th> <th style="text-align: center">date</th> <th style="text-align: center">HUFL</th> <th style="text-align: center">HULL</th> <th style="text-align: center">MUFL</th> <th style="text-align: center">MULL</th> <th style="text-align: center">LUFL</th> <th style="text-align: center">LULL</th> <th style="text-align: center">OT</th> </tr> </thead> <tbody> <tr> <td style="text-align: center">Description</td> <td style="text-align: center">the recorded date</td> <td style="text-align: center">High Useful Load</td> <td style="text-align: center">High Useless Load</td> <td style="text-align: center">Middle Useful Load</td> <td style="text-align: center">Middle Useless Load</td> <td style="text-align: center">Low Useful Load</td> <td style="text-align: center">Low Useless Load</td> <td style="text-align: center">Oil Temperature (target)</td> </tr> </tbody> </table> <p><br/></p> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Cfriday_etth_head5-480.webp 480w,/assets%5Caio2025%5CM03W01%5Cfriday_etth_head5-800.webp 800w,/assets%5Caio2025%5CM03W01%5Cfriday_etth_head5-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Cfriday_etth_head5.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 600px; max-height: 500px; " alt="the first five rows of the ETD" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption"><em>Figure 2.</em>The first five rows of the ETD</figcaption> </figure> <p>First off, this dataset contains <em>temporal data</em> since it is in time series format. Let’s raise some relevant questions and see what charts might resolve them.</p> <table class="language-python"> <tr> <th>Questions</th> <th>Purpose</th> <th>Potential Charts</th> </tr> <tr> <td>Which <u>time</u> has the highest OT?</td> <td><strong>Comparison</strong> (over time)</td> <td> <ul> <li> Line chart </li> <li> Column chart </li> </ul> </td> </tr> <tr> <td>What is the <u>correlation</u> among the variables?</td> <td><strong>Relationship</strong></td> <td> <ul> <li> Scatter plot </li> <li> Bubble chart </li> </ul> </td> </tr> <tr> <td>What is the <u>distribution</u> of the data over time?</td> <td><strong>Distribution</strong></td> <td> <ul> <li> Line chart </li> <li> Histogram </li> <li> Scatter plot </li> </ul> </td> </tr> <tr> <td>What is the <u>percentage</u> of erroneous data if any?</td> <td><strong>Mixed / Composition</strong></td> <td> <ul> <li> Pie chart </li> <li> Stacked bar chart </li> <li> Area chart </li> </ul> </td> </tr> <tr> <td>What is the <u>distribution</u> of average loads for each load type?</td> <td><strong>Distribution</strong></td> <td> <ul> <li> </li> <li> </li> </ul> </td> </tr> </table> <p><br/></p> <h3 id="line-chart"><strong>Line Chart</strong></h3> <p>A <strong>line chart</strong> is used to demonstrate a single or multiple variables according to another variable. It is one of the most common charts utilized in <em>trend analysis</em> and <em>time series analysis</em>.</p> <p>With a large dataset, it’s commonly challenging for audiences to see a specific part if creators only show an entire line chart. We can certainly make a zoom-in period on top of the main chart to highlight such an area.</p> <p><del>DEMONSTRATION</del></p> <h3 id="box-plot"><strong>Box Plot</strong></h3> <p>A <strong>boxplot</strong> or <strong>box and whisker plot</strong> is used to portray the data <em>outliers</em> and distribution with minimum, maximum, and <em>quartiles</em> (excluding outliers) explicitly.</p> <p><del>DEMONSTRATION lke</del></p> <p><strong>Quartiles</strong> are $25\%$, $50\%$, and $75\%$ we talked about in the previous post. We always sort the data before determining these quartiles.</p> <ul> <li>$50\%$ is the median of the entire dataset, denoted <strong>Q2</strong>.</li> <li>$25\%$ is the median of the first half, denoted <strong>Q1</strong>.</li> <li>$75\%$ is the median of the second half, denoted <strong>Q3</strong>.</li> </ul> <p>An <strong>outlier</strong> is a data point that stands out—way out—from the rest. Outliers can have a big impact on how we interpret data. If we’re not careful, they can skew our results and lead us to the wrong conclusions. Take linear regression for example, a dataset with outliers can be pulled in a direction completely different from that without ones. Sometimes they are <em>real valuable</em> but can be just <em>noise</em> other times. Thus, it’s important to identify outliers and delicately handle them.</p> <p>Data points that are not outliers are contained in an <strong>acceptable range</strong>. The formula for it is as follows.</p> \[Q1 - 1.5 \cdot IQR \le \text{Acceptable Range} \le Q3 + 1.5 \cdot IQR\] <p>where $IQR = Q3 - Q1$ is short for <strong>interquartile range</strong>.</p> <p>This formula comes from the idea that outliers should stay outside of the range $(\mu - 3\sigma, \mu + 3\sigma)$ given that</p> <ul> <li>$(\mu - \sigma, \mu + \sigma)$ accounts for 68.27% data points.</li> <li>$(\mu - 2\sigma, \mu + 2\sigma)$ accounts for 95.45% data points.</li> <li>$(\mu - 3\sigma, \mu + 3\sigma)$ accounts for 99.73% data points.</li> </ul> <p>Using these values, we can eventually have that $(\mu - 0.675\sigma, \mu + 0.675\sigma)$ accounts for IQR. If we try some numbers in the place of 1.5, we can conclude that it is actually what we want. This is called the <strong>$1.5$ IQR Rule</strong> created by John Tukey.</p> <p><del>DEMONSTRATION</del> <del>outliers are small hollow circles above the max or below the min (excluding outliers).</del></p> <h3 id="bar-chart"><strong>Bar Chart</strong></h3> <p>A bar chart is used to show the change in the values of a particular variable with respect to others or comparison among mutilple variables. It is also one of the most commonly used charts, generally appearing in discrete or categorical data. It can be horizontal or vertical.</p> <p><del>DEMONSTRATION</del></p> <p>:pushpin: <strong>Note:</strong> There are a couple of tips we can apply when working with data, especially for a bar chart.</p> <ul> <li>When there is a large difference among values of variables, it’s best practice to normalize the data to fit in a range uniformly.</li> <li>The stacked bar chart can be used as a filter to check the relibability of a dataset. That is, we can graph a stacked bar chart to see if there are too many negative/unacceptable values.</li> </ul> <h3 id="donut-chart"><strong>Donut Chart</strong></h3> <p>A <strong>donut chart</strong> is a type of <a href="#pie">pie chart</a> with a hole in the center, looking like a flat donut. This chart can be used to display the proportions of <em>categorical data</em> or check the reliability of a dataset like a stacked bar chart.</p> <p><del>DEMONSTRATION</del></p> <h3 id="correlation-chart"><strong>Correlation Chart</strong></h3> <p>As its name suggests, a <strong>correlation chart</strong> is used to demonstrate the association between pairs of variables. There are two common types of correlation charts: a <em>heatmap</em> and a <em>scatter plot</em>.</p> <p>A <strong>heatmap</strong> is basically a 2D colored table of squares showing the correlation coefficients between every pair of variables. Based on this chart, we can choose property pairs we’re interested in to further the investigation. However, this map is mainly for those who are familiar with data analysis.</p> <p>We can instead use a <strong>pairplot</strong> to exhibit the pairwise bivaritate distrbutions to amateurs. Rather than coefficient numbers, it draws scatter plots’ all possible variable pairs in one chart. Using a pairplot may save time explaining what numerical values mean.</p> <p>Taking one of the graphs from a pairplot, we obtain a <strong>scatter plot</strong> which is illustrate the correlationship between two specific properties.</p> <p><del>DEMONSTRATION</del></p> <h3 id="pie"><strong>Pie Chart</strong></h3> <p>A <strong>pie chart</strong> shows the distribution of a dataset or relative contribution of each variable to the entire set.</p> <p><del>DEMONSTRATION</del></p> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> The ages for everyone in a class are 10, 9, 10, 11, 10, 12, 36, 9. Find outliers if any. <br/> <strong>Exercise 2:</strong> Use the information given about area percentage in the Box Plot section to work out the conclusion about IQR and the constant 1.5. <br/> <strong>Exercise 3:</strong> Practice drawing the given dataset with charts above with librabies. </div> </details> <hr/> <h2 id="case-study-2-student-performance-dataset-spd"><strong>Case Study 2: <a href="https://www.kaggle.com/datasets/spscientist/students-performance-in-exams">Student Performance Dataset (SPD)</a></strong></h2> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Cfriday_sp_head5-480.webp 480w,/assets%5Caio2025%5CM03W01%5Cfriday_sp_head5-800.webp 800w,/assets%5Caio2025%5CM03W01%5Cfriday_sp_head5-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Cfriday_sp_head5.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 600px; max-height: 500px; " alt="the first five rows of the SPD" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption"><em>Figure .</em>The first five rows of the SPD</figcaption> </figure> <h3 id="count-plot"><strong>Count Plot</strong></h3> <p>A <strong>count plot</strong> is a column chart and basically used to count and show the number of observations in a dataset. It is very useful for categorical/non-numerical data points.</p> <p><del>DEMONSTRATION</del></p> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> What questions can you raise for this dataset at a first glance on the first five rows? <br/> <strong>Exercise 2:</strong> For each question, what charts type would you use? <br/> <strong>Exercise 3:</strong> Practice drawing the given dataset with charts above with librabies. </div> </details> <hr/> <h2 id="case-study-3-iris-dataset-ird"><strong>Case Study 3: <a href="https://www.kaggle.com/datasets/saurabh00007/iriscsv">Iris Dataset (IrD)</a></strong></h2> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Cfriday_ir_head5-480.webp 480w,/assets%5Caio2025%5CM03W01%5Cfriday_ir_head5-800.webp 800w,/assets%5Caio2025%5CM03W01%5Cfriday_ir_head5-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Cfriday_ir_head5.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 600px; max-height: 500px; " alt="the first five rows of the IrD" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption"><em>Figure .</em>The first five rows of the IrD</figcaption> </figure> <h3 id="histogram"><strong>Histogram</strong></h3> <p>A <strong>histogram</strong> is used to display the frequency distribution of a variable with horizontal or vertical bins. We came across this chart type when discussing image data.</p> <p>We can apply a histogram to observe both discrete and continuous data. It can be considered as a detailed graph for a boxplot above. Looking at a histogram, we can tell how a dataset is spread out and how skewed it is.</p> <p><del>DEMONSTRATION</del></p> <h3 id="bubble-chart"><strong>Bubble Chart</strong></h3> <p>A <strong>bubble chart</strong> looks similar to a scatter plot but for three variables. It is <em>not</em> a 3D plot; it subtlely corresponds the size of dots on the graph to the <em>third</em> variable.</p> <p><del>DEMONSTRATION</del></p> <h3 id="kde-chart"><strong>KDE Chart</strong></h3> <p>A <strong>Kernel Density Estimate (KDE) plot</strong> can be thought of as a smooth version of one or more histograms in one graph. Instead of using the real values, this plot visualize the <em>probability density</em> of a continuous variable. If there are at least two variables shown, the graph is called a <strong>2D KDE plot</strong>. This chart can be found on the diagonal of a pairplot.</p> <p>This chart is widely exploited in <em>Machine Learning</em>. We will talk more about the mathematical idea behind it in the future.</p> <p><del>DEMONSTRATION</del></p> <h3 id="displot-chart"><strong>Displot Chart</strong></h3> <p>A <strong>displot chart</strong> combines a histogram of a particular variable and the KDE, that is, a distribution curve on top. This can be use for both discrete and continuous data.</p> <p><del>DEMONSTRATION</del></p> <h3 id="3d-visualization"><strong>3D Visualization</strong></h3> <p>A <strong>3D plot</strong> is used to observe the relationship among three numerical variables. Some common 3D plots are <em>scatter plots</em>, <em>surface plots</em>, and <em>contour plots</em>.</p> <p>For two numerical variables and one categorical variable, we just need to use a 2D plot with colors for each numerical values.</p> <p><del>DEMONSTRATION</del></p> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> What questions can you raise for this dataset at a first glance on the first five rows? <br/> <strong>Exercise 2:</strong> For each question, what charts type would you use? <br/> <strong>Exercise 3:</strong> Practice drawing the given dataset with charts above with librabies. </div> </details> <hr/> <h2 id="case-study-4-app-review-dataset-ard"><strong>Case Study 4: App Review Dataset (ARD)</strong></h2> <p>As we know, not all values in a dataset are numerical. For example, this dataset can have the “Rating” column include only numbers, but its “Review” column contains just words. <em>How do we know which words are positive and which ones are negative?</em></p> <h3 id="tag-cloud-chart"><strong>Tag Cloud Chart</strong></h3> <p>In this case, a common solution is to figure out the <em>frequencey</em> or <em>importance</em> of those words. A popular chart that can visualize this is called a <strong>tag cloud</strong>. It provides information about the context of the data. The bigger the word is, the more it appears in the dataset.</p> <p>These kinds of visualization play a critical role in analyzing data from social networking websites and exploring data before performing <em>Natural Language Processing (NLP)</em>.</p> <p><del>DEMONSTRATION</del></p> <h2 id="summary"><strong>Summary</strong></h2> <p>Below are some tips for selecting the proper charts for visualization based on types.</p> <table class="language-python"> <tr> <th>Type</th> <th>Potential Charts</th> </tr> <tr> <td><strong>Showing change over time</strong></td> <td> <ul> <li> Line chart </li> </ul> </td> </tr> <tr> <td><strong>Showing a part-to-whole composition</strong></td> <td> <ul> <li> Pie hcart </li> </ul> </td> </tr> <tr> <td><strong>How data are distributed</strong></td> <td> <ul> <li> KDE plot </li> <li> Displot chart </li> <li> Boxplot </li> </ul> </td> </tr> <tr> <td><strong>Comparing values between groups</strong></td> <td> <ul> <li> Count plot </li> <li> Stacked bar chart </li> </ul> </td> </tr> <tr> <td><strong>Observing relationships between variables</strong></td> <td> <ul> <li> Bubble chart </li> <li> Heatmap </li> </ul> </td> </tr> <tr> <td><strong>Dominant Feature Presentation</strong></td> <td> <ul> <li> Tag cloud chart </li> </ul> </td> </tr> </table> <hr/> <h2 id="speech_balloon-faq">:speech_balloon: <strong>FAQ</strong></h2> <p>Here are some good questions asked during class. The answers to these questions came from either some admin, TA, STA, or members of AIO2025.</p> <table> <thead> <tr> <th>Questions</th> <th>Answers</th> </tr> </thead> <tbody> <tr> <td>Is there anything else I need to learn when doing data visualization?</td> <td>You also need to learn how to choose which visuals to show for audience to understand the gist of your contribution.</td> </tr> <tr> <td>How can I make my visuals colorful and beautiful?</td> <td>You can use tools and combine libraries to make your visualizations beautiful.</td> </tr> <tr> <td>What do I do if a dataset is too large?</td> <td>You need to pick a suitable sample and visualize it, then generalize it to the population.</td> </tr> </tbody> </table> <p><br/></p> ]]></content><author><name></name></author><category term="AI"/><category term="coding"/><category term="AIO"/><summary type="html"><![CDATA[Acknowledgement: This post is based mainly on Vinh Dinh Nguyen’s presentation and slides and partly on on TA, STA, and peers’ responses from the course AIO2025 offered by AI VIETNAM.]]></summary></entry><entry><title type="html">Data Visualization and Analysis for AI 101</title><link href="https://huong-vo.github.io/blog/2025/data-visualization-analysis101/" rel="alternate" type="text/html" title="Data Visualization and Analysis for AI 101"/><published>2025-08-05T00:00:00+00:00</published><updated>2025-08-05T00:00:00+00:00</updated><id>https://huong-vo.github.io/blog/2025/data-visualization-analysis101</id><content type="html" xml:base="https://huong-vo.github.io/blog/2025/data-visualization-analysis101/"><![CDATA[<p><strong>Acknowledgement:</strong> This post is based mainly on Dr. Quang-Vinh Dinh’s presentation and slides and partly on on TA, STA, and peers’ responses from the course AIO2025 offered by AI VIETNAM.</p> <p><strong>Relevance:</strong> The audience of this lecture were those who have read <a href="/blog/2025/math106/">Math for AI 106</a>, had some coding experience, and known a bit of AI knowledge.</p> <p>In today’s post, we’ll explore pandas Series and DataFrame. We’ll also discuss some applications in AI including processing missing values, noise reduction, and data visualization.</p> <hr/> <p>pandas was built on top of NumPy, specifically for data analysis and visualization, like Excel. Since it has NumPy as an underlying structure, we can use what we know about NumPy to apply here.</p> <h2 id="pandas-series"><strong>pandas Series</strong></h2> <p><strong>pandas Series</strong> is a 1D data array like a one-column table. Upone its creation, the numerical indices are <em>automatically generated</em> by default and shown in the rendered output, but they as a whole are not considered as a column. We can update the indices as desired—that is, an index can be any real number, a letter, or other data types.</p> <h3 id="series-creation"><strong>Series Creation</strong></h3> <p><strong>Create a Series from a List</strong> <br/></p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data1</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nc">Series</span><span class="p">([</span><span class="mi">2</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">],</span> <span class="n">name</span><span class="o">=</span><span class="sh">'</span><span class="s">score</span><span class="sh">'</span><span class="p">)</span>
<span class="n">data1</span>

<span class="sh">'''</span><span class="s">
0      2
1      5
2    100
3      7
4      3
5      4
Name: score, dtype: object
</span><span class="sh">'''</span>
</code></pre></div></div> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data2</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nc">Series</span><span class="p">([</span><span class="sh">'</span><span class="s">C++</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Java</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Python</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Golang</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Swift</span><span class="sh">'</span><span class="p">],</span> 
                 <span class="n">index</span><span class="o">=</span><span class="nf">list</span><span class="p">(</span><span class="sh">'</span><span class="s">CJPGS</span><span class="sh">'</span><span class="p">),</span>
                 <span class="n">name</span><span class="o">=</span><span class="sh">'</span><span class="s">Programming Language</span><span class="sh">'</span><span class="p">)</span>
<span class="n">data2</span>

<span class="sh">'''</span><span class="s">
C       C++
J      Java
P    Python
G    Golang
S     Swift
Name: Programming Language, dtype: object
</span><span class="sh">'''</span>
</code></pre></div></div> <p><strong>Get Elements</strong> <br/></p> <p>Use <code class="language-plaintext highlighter-rouge">data1</code>. Just like ndarray in NumPy, we can apply <strong>indexing</strong> and <strong>slicing</strong> for pandas Series to get one or more elements.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># values in a Series is an ndarray
</span><span class="n">data1</span><span class="p">.</span><span class="n">values</span>
<span class="o">&gt;&gt;&gt;</span> <span class="nf">array</span><span class="p">([</span>  <span class="mi">2</span><span class="p">,</span>   <span class="mi">5</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span>   <span class="mi">7</span><span class="p">,</span>   <span class="mi">3</span><span class="p">,</span>   <span class="mi">4</span><span class="p">],</span> <span class="n">dtype</span><span class="o">=</span><span class="n">int64</span><span class="p">)</span>

<span class="c1"># indexing
</span><span class="n">data1</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="o">&gt;&gt;&gt;</span> <span class="mi">5</span>

<span class="c1"># slicing
</span><span class="n">data1</span><span class="p">[</span><span class="mi">2</span><span class="p">:</span><span class="mi">4</span><span class="p">]</span>
<span class="o">&gt;&gt;&gt;</span> <span class="mi">2</span>   <span class="mi">100</span>
    <span class="mi">3</span>     <span class="mi">7</span>
    <span class="n">Name</span><span class="p">:</span> <span class="n">score</span><span class="p">,</span> <span class="n">dtype</span><span class="p">:</span> <span class="nb">object</span>
</code></pre></div></div> <p>A list of indices can be passed as an argument to obtain information from specific rows that may be unevenly-spaced.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data2</span><span class="p">[[</span><span class="sh">'</span><span class="s">C</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">J</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">C</span><span class="sh">'</span><span class="p">]]</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">C</span>     <span class="n">C</span><span class="o">++</span>
    <span class="n">J</span>    <span class="n">Java</span>
    <span class="n">C</span>     <span class="n">C</span><span class="o">++</span>
    <span class="n">Name</span><span class="p">:</span> <span class="n">Programming</span> <span class="n">Language</span><span class="p">,</span> <span class="n">dtype</span><span class="p">:</span> <span class="nb">object</span>
</code></pre></div></div> <p>pandas has other methods to obtain values in a Series like <code class="language-plaintext highlighter-rouge">loc</code> which may be more non-tech-friendly.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data1</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="mi">2</span><span class="p">:</span><span class="mi">4</span><span class="p">]</span>
<span class="o">&gt;&gt;&gt;</span> <span class="mi">2</span>   <span class="mi">100</span>
    <span class="mi">3</span>     <span class="mi">7</span>
    <span class="mi">4</span>     <span class="mi">3</span>
    <span class="n">Name</span><span class="p">:</span> <span class="n">score</span><span class="p">,</span> <span class="n">dtype</span><span class="p">:</span> <span class="nb">object</span>
</code></pre></div></div> <p>Similarly, pandas also has <strong>boolean indexing</strong>.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># boolean indexing
</span><span class="n">data1</span><span class="p">[</span><span class="n">data1</span> <span class="o">&lt;</span> <span class="mi">4</span><span class="p">]</span>
<span class="o">&gt;&gt;&gt;</span> <span class="mi">0</span>    <span class="mi">2</span>
    <span class="mi">4</span>    <span class="mi">3</span>
    <span class="n">Name</span><span class="p">:</span> <span class="n">score</span><span class="p">,</span> <span class="n">dtype</span><span class="p">:</span> <span class="n">int64</span>

<span class="c1"># boolean indexing
</span><span class="n">data1</span><span class="p">[</span><span class="n">data1</span><span class="p">.</span><span class="nf">between</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">7</span><span class="p">)]</span>
<span class="o">&gt;&gt;&gt;</span> <span class="mi">1</span>    <span class="mi">5</span>
    <span class="mi">3</span>    <span class="mi">7</span>
    <span class="n">Name</span><span class="p">:</span> <span class="n">score</span><span class="p">,</span> <span class="n">dtype</span><span class="p">:</span> <span class="n">int64</span>
</code></pre></div></div> <p>For <code class="language-plaintext highlighter-rouge">data2</code> with non-numeric indices, we can either use numeric or customized indices to retrieve elements.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data2</span><span class="p">[</span><span class="sh">'</span><span class="s">C</span><span class="sh">'</span><span class="p">]</span>
<span class="o">&gt;&gt;&gt;</span> <span class="sh">'</span><span class="s">C++</span><span class="sh">'</span>

<span class="n">data2</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="sh">'</span><span class="s">C</span><span class="sh">'</span><span class="p">:</span><span class="sh">'</span><span class="s">J</span><span class="sh">'</span><span class="p">]</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">C</span>     <span class="n">C</span><span class="o">++</span>
    <span class="n">J</span>    <span class="n">Java</span>
    <span class="n">Name</span><span class="p">:</span> <span class="n">Programming</span> <span class="n">Language</span><span class="p">,</span> <span class="n">dtype</span><span class="p">:</span> <span class="nb">object</span>
</code></pre></div></div> <h3 id="operations"><strong>Operations</strong></h3> <p><strong>Drop a Row</strong> <br/> We can remove an existing row by using <code class="language-plaintext highlighter-rouge">drop()</code> with indexing. This method returns a new Series <em>instead of</em> changing the original. <em>Note</em> that the returned Series keeps the original indices.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data1</span><span class="p">.</span><span class="nf">drop</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">])</span>
<span class="o">&gt;&gt;&gt;</span> <span class="mi">0</span>      <span class="mi">2</span>
    <span class="mi">2</span>    <span class="mi">100</span>
    <span class="mi">4</span>      <span class="mi">3</span>
    <span class="mi">5</span>      <span class="mi">4</span>
    <span class="n">Name</span><span class="p">:</span> <span class="n">score</span><span class="p">,</span> <span class="n">dtype</span><span class="p">:</span> <span class="n">int64</span>

<span class="n">data1</span>
<span class="o">&gt;&gt;&gt;</span> <span class="mi">0</span>      <span class="mi">2</span>
    <span class="mi">1</span>      <span class="mi">5</span>
    <span class="mi">2</span>    <span class="mi">100</span>
    <span class="mi">3</span>      <span class="mi">7</span>
    <span class="mi">4</span>      <span class="mi">3</span>
    <span class="mi">5</span>      <span class="mi">4</span>
    <span class="n">Name</span><span class="p">:</span> <span class="n">score</span><span class="p">,</span> <span class="n">dtype</span><span class="p">:</span> <span class="nb">object</span>
</code></pre></div></div> <p><strong>Insert a Row</strong> <br/> Inserting a Row in pandas is similar to that for a Python dictionary. The new row is ed to the original Series or changes an existing row if they have the same index.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data2</span><span class="p">[</span><span class="sh">'</span><span class="s">K</span><span class="sh">'</span><span class="p">]</span> <span class="o">=</span> <span class="sh">'</span><span class="s">Kotlin</span><span class="sh">'</span>
<span class="n">data2</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">C</span>       <span class="n">C</span><span class="o">++</span>
    <span class="n">J</span>      <span class="n">Java</span>
    <span class="n">P</span>    <span class="n">Python</span>
    <span class="n">G</span>    <span class="n">Golang</span>
    <span class="n">S</span>     <span class="n">Swift</span>
    <span class="n">K</span>    <span class="n">Kotlin</span>
    <span class="n">Name</span><span class="p">:</span> <span class="n">Programming</span> <span class="n">Language</span><span class="p">,</span> <span class="n">dtype</span><span class="p">:</span> <span class="nb">object</span>
</code></pre></div></div> <p>If an index is not an integer, we can certainly <strong>reset</strong> the Series so that it is properly indexed. This method does <em>not</em> change the original Series.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data1</span><span class="p">[</span><span class="mf">2.5</span><span class="p">]</span> <span class="o">=</span> <span class="mi">99</span>

<span class="c1"># without drop=True, the old index column is ed
</span><span class="n">data1</span><span class="p">.</span><span class="nf">reset_index</span><span class="p">(</span><span class="n">drop</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="o">&gt;&gt;&gt;</span> <span class="mi">0</span>      <span class="mi">2</span>
    <span class="mi">1</span>      <span class="mi">5</span>
    <span class="mi">2</span>      <span class="mi">100</span>
    <span class="mi">3</span>      <span class="mi">7</span>
    <span class="mi">4</span>      <span class="mi">3</span>
    <span class="mi">5</span>      <span class="mi">4</span>
    <span class="mi">6</span>      <span class="mi">99</span>
    <span class="n">Name</span><span class="p">:</span> <span class="n">score</span><span class="p">,</span> <span class="n">dtype</span><span class="p">:</span> <span class="n">int64</span>  
</code></pre></div></div> <p><strong>Sort a Series</strong> <br/> Sorting a Series by its index can be done with method <code class="language-plaintext highlighter-rouge">sort_index()</code>. We can decide if the original Series should be updated or not by the argument <code class="language-plaintext highlighter-rouge">inplace</code>, in particular,</p> <ul> <li><code class="language-plaintext highlighter-rouge">inplace=True</code> returns None and update the original Series.</li> <li><code class="language-plaintext highlighter-rouge">inplace=False</code> returns a <em>sorted</em> Series and does <em>not</em> update the original Series.</li> </ul> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># update the original Series
</span><span class="n">data2</span><span class="p">.</span><span class="nf">sort_index</span><span class="p">(</span><span class="n">inplace</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">data2</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">C</span>       <span class="n">C</span><span class="o">++</span>
    <span class="n">G</span>    <span class="n">Golang</span>
    <span class="n">J</span>      <span class="n">Java</span>
    <span class="n">K</span>    <span class="n">Kotlin</span>
    <span class="n">P</span>    <span class="n">Python</span>
    <span class="n">S</span>     <span class="n">Swift</span>
    <span class="n">Name</span><span class="p">:</span> <span class="n">Programming</span> <span class="n">Language</span><span class="p">,</span> <span class="n">dtype</span><span class="p">:</span> <span class="nb">object</span>
</code></pre></div></div> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># NOT update the original Series
</span><span class="n">sorted_data2</span> <span class="o">=</span> <span class="n">data2</span><span class="p">.</span><span class="nf">sort_index</span><span class="p">(</span><span class="n">inplace</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
<span class="n">sorted_data2</span> 
<span class="o">&gt;&gt;&gt;</span> <span class="n">C</span>       <span class="n">C</span><span class="o">++</span>
    <span class="n">G</span>    <span class="n">Golang</span>
    <span class="n">J</span>      <span class="n">Java</span>
    <span class="n">K</span>    <span class="n">Kotlin</span>
    <span class="n">P</span>    <span class="n">Python</span>
    <span class="n">S</span>     <span class="n">Swift</span>
    <span class="n">Name</span><span class="p">:</span> <span class="n">Programming</span> <span class="n">Language</span><span class="p">,</span> <span class="n">dtype</span><span class="p">:</span> <span class="nb">object</span>

<span class="n">data2</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">C</span>       <span class="n">C</span><span class="o">++</span>
    <span class="n">J</span>      <span class="n">Java</span>
    <span class="n">P</span>    <span class="n">Python</span>
    <span class="n">G</span>    <span class="n">Golang</span>
    <span class="n">S</span>     <span class="n">Swift</span>
    <span class="n">K</span>    <span class="n">Kotlin</span>
    <span class="n">Name</span><span class="p">:</span> <span class="n">Programming</span> <span class="n">Language</span><span class="p">,</span> <span class="n">dtype</span><span class="p">:</span> <span class="nb">object</span>
</code></pre></div></div> <p><strong>Common Functions</strong> <br/> pandas contains NumPy’s common functions</p> <ul> <li><code class="language-plaintext highlighter-rouge">series.min()</code></li> <li><code class="language-plaintext highlighter-rouge">series.max()</code></li> <li><code class="language-plaintext highlighter-rouge">series.idxmin()</code> or <code class="language-plaintext highlighter-rouge">series.argmin()</code></li> <li><code class="language-plaintext highlighter-rouge">series.idxmax()</code> or <code class="language-plaintext highlighter-rouge">series.argmax()</code></li> <li><code class="language-plaintext highlighter-rouge">series.sum()</code></li> <li><code class="language-plaintext highlighter-rouge">series.mean()</code></li> <li><code class="language-plaintext highlighter-rouge">series.var()</code></li> <li><code class="language-plaintext highlighter-rouge">series.std()</code></li> <li><code class="language-plaintext highlighter-rouge">series.str.count()</code>: the argument can be a character or a string and it counts the number of characters or strings in each value.</li> <li><code class="language-plaintext highlighter-rouge">series.str.upper()</code></li> <li><code class="language-plaintext highlighter-rouge">series.replace(current_value, new_value)</code>: replace the current value with a new value.</li> </ul> <p>** Two Series** <br/> If values from Series are of the same data type, they can be ed or concatenated. Behind the scenes, pandas looks for the matching index and two values together. If it <em>cannot</em> find a row’s matching index, it will render <code class="language-plaintext highlighter-rouge">NaN</code>.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># modify data1 to have 6 rows
</span><span class="n">data1</span><span class="p">[</span><span class="mf">2.5</span><span class="p">]</span> <span class="o">=</span> <span class="mi">99</span>
<span class="n">data1</span><span class="p">.</span><span class="nf">sort_index</span><span class="p">(</span><span class="n">inplace</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>

<span class="c1"># create data3 of 5 rows
</span><span class="n">data3</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nc">Series</span><span class="p">([</span><span class="mi">10</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">10</span><span class="p">])</span>

<span class="c1">#  data1 and data3
</span><span class="n">data1</span> <span class="o">+</span> <span class="n">data3</span>
<span class="o">&gt;&gt;&gt;</span> <span class="mf">0.0</span>     <span class="mf">12.0</span>
    <span class="mf">1.0</span>     <span class="mf">15.0</span>
    <span class="mf">2.0</span>    <span class="mf">110.0</span>
    <span class="mf">2.5</span>      <span class="n">NaN</span>
    <span class="mf">3.0</span>     <span class="mf">17.0</span>
    <span class="mf">4.0</span>     <span class="mf">13.0</span>
    <span class="mf">5.0</span>      <span class="n">NaN</span>
<span class="n">dtype</span><span class="p">:</span> <span class="n">float64</span>
</code></pre></div></div> <p><strong>Group Values</strong> <br/> If some values have the same name/index and we want to group them together, we can use <code class="language-plaintext highlighter-rouge">groupby()</code>. This syntax returns another Series, so we can certainly use Series methods or function on it.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data4</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nc">Series</span><span class="p">([</span><span class="mi">4</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">8</span><span class="p">],</span>
                  <span class="n">index</span><span class="o">=</span><span class="nf">list</span><span class="p">([</span><span class="sh">'</span><span class="s">C++</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Golang</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Java</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Golang</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Java</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">C++</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Python</span><span class="sh">'</span><span class="p">]),</span>
                  <span class="n">name</span><span class="o">=</span><span class="sh">'</span><span class="s">groupby</span><span class="sh">'</span><span class="p">)</span>

<span class="c1"># test
</span><span class="n">data4</span><span class="p">.</span><span class="nf">groupby</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="mi">0</span><span class="p">).</span><span class="nf">sum</span><span class="p">()</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">C</span><span class="o">++</span>         <span class="mi">5</span>
    <span class="n">Golang</span>      <span class="mi">3</span>
    <span class="n">Java</span>        <span class="mi">2</span>
    <span class="n">Python</span>      <span class="mi">8</span>
    <span class="n">Name</span><span class="p">:</span> <span class="n">groupby</span><span class="p">,</span> <span class="n">dtype</span><span class="p">:</span> <span class="n">int64</span>

<span class="n">data4</span><span class="p">.</span><span class="nf">groupby</span><span class="p">(</span><span class="n">data4</span> <span class="o">&gt;</span> <span class="mi">3</span><span class="p">).</span><span class="nf">sum</span><span class="p">()</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">groupby</span>
    <span class="bp">False</span>     <span class="mi">6</span>
    <span class="bp">True</span>     <span class="mi">12</span>
    <span class="n">Name</span><span class="p">:</span> <span class="n">groupby</span><span class="p">,</span> <span class="n">dtype</span><span class="p">:</span> <span class="n">int64</span>

<span class="n">data4</span><span class="p">[</span><span class="sh">'</span><span class="s">Golang</span><span class="sh">'</span><span class="p">].</span><span class="nf">count</span><span class="p">()</span>
<span class="o">&gt;&gt;&gt;</span> <span class="mi">2</span>
</code></pre></div></div> <h3 id="bulb-application-in-ai">:bulb: <strong>Application in AI</strong></h3> <p><strong>1D Interpolation</strong> <br/></p> <ol> <li>The problem introduced here appears in scaling images and the two techniques are a part of <em>Super-resolution Imaging</em>. Since scaling an image increases the number of cells but newly ed cells need values.</li> <li>Another situation is when we work with <em>missing</em> data points. We can either remove or keep them depending on the type of datasets. For those that stay, we need to make sure they have values. For example, time series data can be related with each other, so we’d like to keep missing values.</li> </ol> <p>In both cases, we need to fill in context-appropriate rather than random values. For some datasets, we fill in with the average. In ition, we use <code class="language-plaintext highlighter-rouge">interpolate()</code> with a method argument to resolve this issue.</p> <ul> <li><strong>Linear Interpolation:</strong> <code class="language-plaintext highlighter-rouge">method='linear'</code> by default.</li> <li><strong>Nearest Neighbor Interpolation:</strong> <code class="language-plaintext highlighter-rouge">method='nearest'</code>.</li> </ul> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data5</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nc">Series</span><span class="p">([</span><span class="mi">2</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="n">np</span><span class="p">.</span><span class="n">nan</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="n">np</span><span class="p">.</span><span class="n">nan</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="n">np</span><span class="p">.</span><span class="n">nan</span><span class="p">],</span> <span class="n">name</span><span class="o">=</span><span class="sh">'</span><span class="s">score</span><span class="sh">'</span><span class="p">)</span>

<span class="c1"># linear
</span><span class="n">data5</span><span class="p">.</span><span class="nf">interpolate</span><span class="p">(</span><span class="n">method</span><span class="o">=</span><span class="sh">'</span><span class="s">linear</span><span class="sh">'</span><span class="p">)</span>

<span class="c1"># nearest neighbor
</span><span class="n">data5</span><span class="p">.</span><span class="nf">interpolate</span><span class="p">(</span><span class="n">method</span><span class="o">=</span><span class="sh">'</span><span class="s">nearest</span><span class="sh">'</span><span class="p">)</span>
</code></pre></div></div> <p>In the example above, pandas considered 100 and 7 for the first <code class="language-plaintext highlighter-rouge">np.nan</code>.</p> <figure> <div style="display: flex; flex-wrap:wrap; column-gap: 20px; justify-content: center"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Cwednesday_weather_1Dinterpolation-480.webp 480w,/assets%5Caio2025%5CM03W01%5Cwednesday_weather_1Dinterpolation-800.webp 800w,/assets%5Caio2025%5CM03W01%5Cwednesday_weather_1Dinterpolation-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Cwednesday_weather_1Dinterpolation.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 400px; max-height: 450px; " alt="original data with NaN" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">Original data with NaN</figcaption> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Cwednesday_weather_linear-480.webp 480w,/assets%5Caio2025%5CM03W01%5Cwednesday_weather_linear-800.webp 800w,/assets%5Caio2025%5CM03W01%5Cwednesday_weather_linear-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Cwednesday_weather_linear.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 320px; max-height: 450px; " alt="data after applying linear interpolation" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">Data after applying linear interpolation</figcaption> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Cwednesday_weather_nearest_neighbor-480.webp 480w,/assets%5Caio2025%5CM03W01%5Cwednesday_weather_nearest_neighbor-800.webp 800w,/assets%5Caio2025%5CM03W01%5Cwednesday_weather_nearest_neighbor-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Cwednesday_weather_nearest_neighbor.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 320px; max-height: 450px; " alt="data after applying nearest neighbor interpolation" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">Data after applying nearest neighbor interpolation</figcaption> </figure> </div> <figcaption style="text-align: center; width: 100%; font-size:0.875em; margin-top:-25px;"> <em>Figure 1.</em> The first 100 data points of a weather dataset before and after interpolation techniques </figcaption> </figure> <details class="bordered-block"> <summary> <strong>Nearest Neighbor Interpolation vs. Linear Interpolation</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Problem statement:</strong> Assume there are two data points, find another data point given one of its coordinates. <br/> <strong>Breakdown:</strong> <ul> <li> $x$ is the index, $y$ is the value in a Series. </li> <li> $(x_1, y_1)$ and $(x_2, y_2)$: two given data points. </li> <li> $x_3$: known coordinate of the third data point. </li> <li> $y_3$: unknown coordinate of the third data point. </li> </ul> <strong>Idea:</strong> There are two simple but effective techniques: <em>nearest neighbor interpolation</em> and <em>linear interpolation</em>. <table class="language-python"> <tr> <th></th> <th>Nearest Neighbor</th> <th>Interpolation</th> </tr> <tr> <th scope="row">Solution</th> <td class="centered_td"> <ul> <li> Compute distance from $x_3$ to $x_1$ and $x_2$. </li> <li> Pick the y-value of the smaller one. </li> </ul> </td> <td class="centered_td"> <ul> <li> Find the linear equation passing $(x_1, y_1)$ and $(x_2, y_2)$. </li> <li> Take the corresponding y-value on that line. </li> $$\begin{align*} &amp;y_3 = \frac{x_2 - x_3}{x_2 - x_1} y_1 \\ &amp;+ \frac{x_3 - x_1}{x_2 - x_1} y_2 \end{align*}$$ </ul> </td> </tr> <tr> <th scope="row">Application</th> <td class="centered_td"> <ul> <li> If objects in an image are straight up perpendicular to the edges, they should look fine when the image is scaled. </li> <li> If they are slanted, they could look like a floor function, Lego blocks, or images in Minecraft. </li> </ul> </td> <td class="centered_td">This can be better for slanted objects.</td> </tr> </table> <br/> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Cwednesday_1Dinterpolation_nearest_neighbor-480.webp 480w,/assets%5Caio2025%5CM03W01%5Cwednesday_1Dinterpolation_nearest_neighbor-800.webp 800w,/assets%5Caio2025%5CM03W01%5Cwednesday_1Dinterpolation_nearest_neighbor-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Cwednesday_1Dinterpolation_nearest_neighbor.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 320px; max-height: 450px; " alt="image scaling using nearest neighbor technique" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">Image scaling using nearest neighbor technique</figcaption> </figure> <figure style="margin-top: 0; ping-top: 0;"> <iframe src="https://www.geogebra.org/classic/mhxnhwws?embed" title="Interactive GeoGebra applet showing Nearest Neighbor and Linear Interpolation." frameborder="0" height="700px" width="100%" style="border: 1px solid #ddd; margin-top: 0; ping-top: 0;" allowfullscreen="">Your browser does not support iframes. View the interactive applet <a href="https://www.geogebra.org/classic/mhxnhwws" target="_blank" rel="noopener">here</a>.</iframe> <figcaption style="font-size:0.875em"> <em>Figure 2.</em> $f(x)$ represents the nearest neighbor interpolation. $g(x) = ax + b$ represents linear interpolation. Move the sliders for points $A$ and $B$ to set their y-values, then drag the slider point along the x-axis to choose the input $x$. The applet updates $f(x)$ and $g(x)$ to show the estimated values using each methodView the interactive applet. Click on the symbol in the bottom right corner to open the applet in full-screen mode. If the applet does not load, you can open it directly <a href="https://www.geogebra.org/classic/mhxnhwws" target="_blank" rel="noopener">here</a>.</figcaption> </figure> </div> </details> <p><br/></p> <p><strong>Noise Reduction</strong> <br/> <strong>Noise</strong> refers to the random irregularity or fluctuations in real-life datasets. There are a few ways to <strong>reduce noise</strong> in statistics.</p> <p><strong>Simple Moving Average (SMA):</strong> the simplest way in which we take the mean of data windows.</p> \[SMA_t = \frac{s_t + \cdots + s_{t-k+1}}{k}\] <p>where $k$ is the size of each window.</p> <p><strong>Exponential Moving/Weighted Average (EMA):</strong> widely used in optimization, considering both the previously computed values and the current value.</p> \[EMA_t = \rho EMA_{t-1} + (1 - \rho) s_t\] <p>Later on, we’ll mainly use EMA, but for now let’s look at the graph of the above dataset after using SMA. The <em>larger</em> the window is, the <em>smoother</em> the graph is.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">linear_sma_5</span> <span class="o">=</span> <span class="n">linear_int</span><span class="p">.</span><span class="nf">rolling</span><span class="p">(</span><span class="n">window</span><span class="o">=</span><span class="mi">5</span><span class="p">).</span><span class="nf">mean</span><span class="p">()</span>
<span class="n">linear_sma_5</span><span class="p">.</span><span class="nf">head</span><span class="p">(</span><span class="mi">100</span><span class="p">).</span><span class="nf">plot</span><span class="p">()</span>
</code></pre></div></div> <figure> <div style="display: flex; flex-wrap:wrap; column-gap: 20px; justify-content: center"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Cwednesday_weather_linear_sma_window=3-480.webp 480w,/assets%5Caio2025%5CM03W01%5Cwednesday_weather_linear_sma_window=3-800.webp 800w,/assets%5Caio2025%5CM03W01%5Cwednesday_weather_linear_sma_window=3-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Cwednesday_weather_linear_sma_window=3.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 320px; max-height: 450px; " alt="window=3" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">window=3</figcaption> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Cwednesday_weather_linear_sma_window=5-480.webp 480w,/assets%5Caio2025%5CM03W01%5Cwednesday_weather_linear_sma_window=5-800.webp 800w,/assets%5Caio2025%5CM03W01%5Cwednesday_weather_linear_sma_window=5-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Cwednesday_weather_linear_sma_window=5.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 320px; max-height: 450px; " alt="window=5" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">window=5</figcaption> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Cwednesday_weather_linear_sma_window=10-480.webp 480w,/assets%5Caio2025%5CM03W01%5Cwednesday_weather_linear_sma_window=10-800.webp 800w,/assets%5Caio2025%5CM03W01%5Cwednesday_weather_linear_sma_window=10-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Cwednesday_weather_linear_sma_window=10.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 320px; max-height: 450px; " alt="window=10" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">window=10</figcaption> </figure> </div> <figcaption style="text-align:center; width:100%; font-size:0.875em; margin-top:-25px;"> <em>Figure 3.</em> The first 100 data points of a weather dataset before and after applying linear interpolation and SMA </figcaption> </figure> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> For <code>data2</code>, what is the output of <code>data2['D']</code> and <code>data2['C':'X']</code>? Explain why. <br/> <strong>Exercise 2:</strong> Work out the formulae for linear interpolation technique. <br/> <strong>Exercise 3:</strong> Which values does pandas consider for second and third <code>np.nan</code> in each case of the interpolation example? <br/> <strong>Exercise 4:</strong> Write code for the two other windows of the SMA example (linear and nearest neighbor). </div> </details> <hr/> <h2 id="dataframe"><strong>DataFrame</strong></h2> <p>A <strong>DataFrame</strong> is a 2D labeled data structure. Unlike a Series, a DateFrame can have more than one data column. Basically, it is a combination of multiple Series. Thus, we can apply methods/functions in Series to work with DataFrames.</p> <p><strong>Get Values</strong> <br/> We can also apply <strong>indexing</strong> and <strong>slicing</strong> for pandas Series to get one or more elements like a NumPy array—using a comma to separate axes.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nf">read_csv</span><span class="p">(</span><span class="sh">'</span><span class="s">advertising_simple.csv</span><span class="sh">'</span><span class="p">)</span>
<span class="n">df</span>
<span class="o">&gt;&gt;&gt;</span>     <span class="n">TV</span>  <span class="n">Radio</span>   <span class="n">Newspaper</span>   <span class="n">Sales</span>
    <span class="mi">0</span>   <span class="mi">44</span>     <span class="mi">39</span>          <span class="mi">45</span>      <span class="mi">10</span>
    <span class="mi">1</span>   <span class="mi">17</span>     <span class="mi">45</span>          <span class="mi">69</span>      <span class="mi">12</span>
    <span class="mi">2</span>   <span class="mi">151</span>    <span class="mi">41</span>          <span class="mi">58</span>      <span class="mi">16</span>
    <span class="mi">3</span>   <span class="mi">180</span>    <span class="mi">10</span>          <span class="mi">58</span>      <span class="mi">17</span>
    <span class="mi">4</span>   <span class="mi">8</span>      <span class="mi">48</span>          <span class="mi">75</span>      <span class="mi">7</span>
    <span class="mi">5</span>   <span class="mi">57</span>     <span class="mi">32</span>          <span class="mi">23</span>      <span class="mi">11</span>
    <span class="mi">6</span>   <span class="mi">120</span>    <span class="mi">19</span>          <span class="mi">11</span>      <span class="mi">13</span>
    <span class="mi">7</span>   <span class="mi">8</span>      <span class="mi">2</span>           <span class="mi">1</span>       <span class="mi">4</span>

<span class="c1"># get 'Radio' column
</span><span class="n">df</span><span class="p">.</span><span class="n">loc</span><span class="p">[:,</span> <span class="sh">'</span><span class="s">Radio</span><span class="sh">'</span><span class="p">]</span>
<span class="o">&gt;&gt;&gt;</span> <span class="mi">0</span>     <span class="mi">39</span>
    <span class="mi">1</span>     <span class="mi">45</span>
    <span class="mi">2</span>     <span class="mi">41</span>
    <span class="mi">3</span>     <span class="mi">10</span>
    <span class="mi">4</span>     <span class="mi">48</span>
    <span class="mi">5</span>     <span class="mi">32</span>
    <span class="mi">6</span>     <span class="mi">19</span>
    <span class="mi">7</span>      <span class="mi">2</span>
    <span class="n">Name</span><span class="p">:</span> <span class="n">Radio</span><span class="p">,</span> <span class="n">dtype</span><span class="p">:</span> <span class="n">int64</span>

<span class="c1"># get a DataFrame of 'Radio' column
</span><span class="n">df</span><span class="p">.</span><span class="n">loc</span><span class="p">[:,</span> <span class="p">[</span><span class="sh">'</span><span class="s">Radio</span><span class="sh">'</span><span class="p">]]</span>
<span class="o">&gt;&gt;&gt;</span>    <span class="n">Radio</span>
    <span class="mi">0</span>     <span class="mi">39</span>
    <span class="mi">1</span>     <span class="mi">45</span>
    <span class="mi">2</span>     <span class="mi">41</span>
    <span class="mi">3</span>     <span class="mi">10</span>
    <span class="mi">4</span>     <span class="mi">48</span>
    <span class="mi">5</span>     <span class="mi">32</span>
    <span class="mi">6</span>     <span class="mi">19</span>
    <span class="mi">7</span>      <span class="mi">2</span>

<span class="c1"># another way to get 'Radio' column
</span><span class="n">df</span><span class="p">.</span><span class="n">Radio</span>
<span class="o">&gt;&gt;&gt;</span> <span class="mi">0</span>     <span class="mi">39</span>
    <span class="mi">1</span>     <span class="mi">45</span>
    <span class="mi">2</span>     <span class="mi">41</span>
    <span class="mi">3</span>     <span class="mi">10</span>
    <span class="mi">4</span>     <span class="mi">48</span>
    <span class="mi">5</span>     <span class="mi">32</span>
    <span class="mi">6</span>     <span class="mi">19</span>
    <span class="mi">7</span>      <span class="mi">2</span>
    <span class="n">Name</span><span class="p">:</span> <span class="n">Radio</span><span class="p">,</span> <span class="n">dtype</span><span class="p">:</span> <span class="n">int64</span>
</code></pre></div></div> <p>Besides <code class="language-plaintext highlighter-rouge">loc</code>, pandas has <code class="language-plaintext highlighter-rouge">iloc</code> to get values using index rather than label. Similar to NumPy, pandas has <strong>boolean indexing</strong>.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">df</span><span class="p">.</span><span class="n">Sales</span> <span class="o">&gt;</span> <span class="mi">10</span><span class="p">]</span>
<span class="o">&gt;&gt;&gt;</span>     <span class="n">TV</span>  <span class="n">Radio</span>   <span class="n">Newspaper</span>   <span class="n">Sales</span>
    <span class="mi">1</span>   <span class="mi">17</span>     <span class="mi">45</span>          <span class="mi">69</span>      <span class="mi">12</span>
    <span class="mi">2</span>   <span class="mi">151</span>    <span class="mi">41</span>          <span class="mi">58</span>      <span class="mi">16</span>
    <span class="mi">3</span>   <span class="mi">180</span>    <span class="mi">10</span>          <span class="mi">58</span>      <span class="mi">17</span>
    <span class="mi">5</span>   <span class="mi">57</span>     <span class="mi">32</span>          <span class="mi">23</span>      <span class="mi">11</span>
    <span class="mi">6</span>   <span class="mi">120</span>    <span class="mi">19</span>          <span class="mi">11</span>      <span class="mi">13</span>
</code></pre></div></div> <p><strong>Sort Values</strong> <br/> pandas lets us sort data based on one or multiple columns sequentially.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># sort by one column
</span><span class="n">df</span><span class="p">.</span><span class="nf">sort_values</span><span class="p">([</span><span class="sh">'</span><span class="s">Newspaper</span><span class="sh">'</span><span class="p">])</span>

<span class="c1"># sort by more than one column
# 'Newspaper' first, then based on that, 'Sales'
</span><span class="n">df</span><span class="p">.</span><span class="nf">sort_values</span><span class="p">([</span><span class="sh">'</span><span class="s">Newspaper</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Sales</span><span class="sh">'</span><span class="p">])</span>
</code></pre></div></div> <p><strong>Inser a Column</strong> <br/> We can insert a column to a DataFrame like ing an item in Python dictionary. The original DataFrame is updated. Besides the default index ‘colum’, we can certainly set a column to be indices. Note that setting indices does not affect the original.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#  'ID' column
</span><span class="n">df</span><span class="p">[</span><span class="sh">'</span><span class="s">ID</span><span class="sh">'</span><span class="p">]</span> <span class="o">=</span> <span class="nf">list</span><span class="p">(</span><span class="nf">range</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">10</span><span class="p">))</span>

<span class="c1"># set index
</span><span class="n">df</span> <span class="o">=</span> <span class="n">df</span><span class="p">.</span><span class="nf">set_index</span><span class="p">(</span><span class="sh">'</span><span class="s">ID</span><span class="sh">'</span><span class="p">)</span>
<span class="n">df</span>
<span class="o">&gt;&gt;&gt;</span>     <span class="n">TV</span>  <span class="n">Radio</span>   <span class="n">Newspaper</span>   <span class="n">Sales</span>
    <span class="n">ID</span>				
    <span class="mi">2</span>   <span class="mi">44</span>     <span class="mi">39</span>          <span class="mi">45</span>      <span class="mi">10</span>
    <span class="mi">3</span>   <span class="mi">17</span>     <span class="mi">45</span>          <span class="mi">69</span>      <span class="mi">12</span>
    <span class="mi">4</span>   <span class="mi">151</span>    <span class="mi">41</span>          <span class="mi">58</span>      <span class="mi">16</span>
    <span class="mi">5</span>   <span class="mi">180</span>    <span class="mi">10</span>          <span class="mi">58</span>      <span class="mi">17</span>
    <span class="mi">6</span>   <span class="mi">8</span>      <span class="mi">48</span>          <span class="mi">75</span>      <span class="mi">7</span>
    <span class="mi">7</span>   <span class="mi">57</span>     <span class="mi">32</span>          <span class="mi">23</span>      <span class="mi">11</span>
    <span class="mi">8</span>   <span class="mi">120</span>    <span class="mi">19</span>          <span class="mi">11</span>      <span class="mi">13</span>
    <span class="mi">9</span>   <span class="mi">8</span>      <span class="mi">2</span>           <span class="mi">1</span>       <span class="mi">4</span>
</code></pre></div></div> <p><strong>Concatenate Rows</strong> <br/> If we want to combine two DataFrames with the same columns together, we use <code class="language-plaintext highlighter-rouge">concat([dataFrame1, dataFrame2])</code>. Since the original indices from each DataFrame remain unchanged, we may need to reset index and drop the old one by <code class="language-plaintext highlighter-rouge">reset_index(drop=True)</code>.</p> <p><strong>Datetime</strong> <br/> When data is collected, time is often inserted as strings, which makes it harder for computers to understand and analyze. Thus, it’s recommended time data be converted to real datetime format. Use <code class="language-plaintext highlighter-rouge">to_datetime()</code> method.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nf">read_csv</span><span class="p">(</span><span class="sh">'</span><span class="s">WeatherHistory2D.csv</span><span class="sh">'</span><span class="p">)</span>

<span class="c1"># convert string to datetime format
</span><span class="n">time_col</span> <span class="o">=</span> <span class="sh">'</span><span class="s">Formatted Date</span><span class="sh">'</span>
<span class="n">df</span><span class="p">[</span><span class="n">time_col</span><span class="p">]</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nf">to_datetime</span><span class="p">(</span><span class="n">df</span><span class="p">[</span><span class="n">time_col</span><span class="p">],</span>
                              <span class="n">utc</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>

<span class="c1"># get each feature of datetime 
#  a column for each feature
</span><span class="n">df</span><span class="p">[</span><span class="sh">'</span><span class="s">Year</span><span class="sh">'</span><span class="p">]</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="n">time_col</span><span class="p">].</span><span class="n">dt</span><span class="p">.</span><span class="n">year</span>
<span class="n">df</span><span class="p">[</span><span class="sh">'</span><span class="s">Month</span><span class="sh">'</span><span class="p">]</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="n">time_col</span><span class="p">].</span><span class="n">dt</span><span class="p">.</span><span class="n">month</span>
<span class="n">df</span><span class="p">[</span><span class="sh">'</span><span class="s">Day</span><span class="sh">'</span><span class="p">]</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="n">time_col</span><span class="p">].</span><span class="n">dt</span><span class="p">.</span><span class="n">day</span>
<span class="n">df</span><span class="p">[</span><span class="sh">'</span><span class="s">Hour</span><span class="sh">'</span><span class="p">]</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="n">time_col</span><span class="p">].</span><span class="n">dt</span><span class="p">.</span><span class="n">hour</span>
</code></pre></div></div> <p><strong>Remove a Column</strong> <br/> In the example above, the “Formatted Date” column is no longer needed as we separate columns for datetime features. We can just delete it using <code class="language-plaintext highlighter-rouge">df.drop(label, axis)</code>. This syntax <em>returns</em> a new DataFrame rather than changing the original. We can certainly delete <em>multiple</em> columns.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">df</span><span class="p">.</span><span class="nf">drop</span><span class="p">(</span><span class="n">time_col</span><span class="p">,</span> <span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="o">&gt;&gt;&gt;</span>     <span class="nc">Temperature </span><span class="p">(</span><span class="n">C</span><span class="p">)</span> <span class="n">Year</span>    <span class="n">Month</span>   <span class="n">Day</span> <span class="n">Hour</span>
    <span class="mi">0</span>      <span class="mf">0.577778</span>     <span class="mi">2005</span>       <span class="mi">12</span>    <span class="mi">31</span>   <span class="mi">23</span>
    <span class="mi">1</span>      <span class="mf">1.161111</span>     <span class="mi">2006</span>       <span class="mi">1</span>     <span class="mi">1</span>    <span class="mi">0</span>
</code></pre></div></div> <p><strong>Group Values</strong> <br/> We can certain group values by columns. That is, if there are duplicate rows in a column, we can combine them together and use functions to analyze each of them as a whole.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">df_group</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nf">read_csv</span><span class="p">(</span><span class="sh">'</span><span class="s">weatherHistory_simple.csv</span><span class="sh">'</span><span class="p">)</span>
<span class="n">df_group</span>
<span class="o">&gt;&gt;&gt;</span>     <span class="n">Precip</span> <span class="n">Type</span> <span class="nc">Temperature </span><span class="p">(</span><span class="n">C</span><span class="p">)</span>
    <span class="mi">0</span>          <span class="n">rain</span>            <span class="mf">12.2</span>
    <span class="mi">1</span>          <span class="n">rain</span>            <span class="mf">13.8</span>
    <span class="mi">2</span>          <span class="n">rain</span>            <span class="mf">14.7</span>
    <span class="mi">3</span>          <span class="n">rain</span>            <span class="mf">13.8</span>
    <span class="mi">4</span>          <span class="n">rain</span>            <span class="mf">12.7</span>
    <span class="mi">5</span>          <span class="n">rain</span>            <span class="mf">0.5</span>
    <span class="mi">6</span>          <span class="n">snow</span>            <span class="o">-</span><span class="mf">0.4</span>
    <span class="mi">7</span>          <span class="n">snow</span>            <span class="o">-</span><span class="mf">1.1</span>
    <span class="mi">8</span>          <span class="n">snow</span>            <span class="o">-</span><span class="mf">1.6</span>
    <span class="mi">9</span>          <span class="n">snow</span>            <span class="o">-</span><span class="mf">2.1</span>

<span class="c1"># group by 'Precip Type' column and compute mean of each feature by 'Temperature (C)' column
</span><span class="n">df_group</span><span class="p">.</span><span class="nf">groupby</span><span class="p">(</span><span class="sh">'</span><span class="s">Precip Type</span><span class="sh">'</span><span class="p">).</span><span class="nf">mean</span><span class="p">(</span><span class="sh">'</span><span class="s">Temperature (C)</span><span class="sh">'</span><span class="p">)</span>
<span class="o">&gt;&gt;&gt;</span>         <span class="nc">Temperature </span><span class="p">(</span><span class="n">C</span><span class="p">)</span>
<span class="n">Precip</span> <span class="n">Type</span>	
<span class="n">rain</span>              <span class="mf">11.283333</span>
<span class="n">snow</span>              <span class="o">-</span><span class="mf">1.300000</span>
</code></pre></div></div> <p><strong>Convert Categories to Numbers</strong> <br/> In the example above, the “Precip Type” column can be a <em>class</em> column as in a <em>classification problem</em>. However, computers can only work with numeric values, it’s better that we convert those string categories into numbers.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># test
</span><span class="n">col_to_num</span> <span class="o">=</span> <span class="sh">'</span><span class="s">Precip Type</span><span class="sh">'</span>
<span class="n">pd</span><span class="p">.</span><span class="nc">Categorical</span><span class="p">(</span><span class="n">df_group</span><span class="p">[</span><span class="n">col_to_num</span><span class="p">])</span>
<span class="o">&gt;&gt;&gt;</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
    <span class="nc">Categories </span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">int8</span><span class="p">):</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>

<span class="n">pd</span><span class="p">.</span><span class="nc">Categorical</span><span class="p">(</span><span class="n">df_group</span><span class="p">[</span><span class="n">col_to_num</span><span class="p">]).</span><span class="n">codes</span>
<span class="o">&gt;&gt;&gt;</span> <span class="nf">array</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">],</span> <span class="n">dtype</span><span class="o">=</span><span class="n">int8</span><span class="p">)</span>

<span class="c1"># convert categories to numbers
</span><span class="n">col_to_num</span> <span class="o">=</span> <span class="sh">'</span><span class="s">Precip Type</span><span class="sh">'</span>
<span class="n">df_group</span><span class="p">[</span><span class="n">col_to_num</span><span class="p">]</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nc">Categorical</span><span class="p">(</span><span class="n">df_group</span><span class="p">[</span><span class="n">col_to_num</span><span class="p">]).</span><span class="n">codes</span>
<span class="n">df_group</span>
<span class="o">&gt;&gt;&gt;</span>     <span class="n">Precip</span> <span class="n">Type</span> <span class="nc">Temperature </span><span class="p">(</span><span class="n">C</span><span class="p">)</span>
    <span class="mi">0</span>             <span class="mi">1</span>           <span class="mf">12.2</span>
    <span class="mi">1</span>             <span class="mi">1</span>           <span class="mf">13.8</span>
    <span class="mi">2</span>             <span class="mi">1</span>           <span class="mf">14.7</span>
    <span class="mi">3</span>             <span class="mi">1</span>           <span class="mf">13.8</span>
    <span class="mi">4</span>             <span class="mi">1</span>           <span class="mf">12.7</span>
    <span class="mi">5</span>             <span class="mi">1</span>           <span class="mf">0.5</span>
    <span class="mi">6</span>             <span class="mi">0</span>           <span class="o">-</span><span class="mf">0.4</span>
    <span class="mi">7</span>             <span class="mi">0</span>           <span class="o">-</span><span class="mf">1.1</span>
    <span class="mi">8</span>             <span class="mi">0</span>           <span class="o">-</span><span class="mf">1.6</span>
    <span class="mi">9</span>             <span class="mi">0</span>           <span class="o">-</span><span class="mf">2.1</span>
</code></pre></div></div> <p><strong>Descriptive Statistics</strong> <br/> We can check out all the basic information related to a dataset by <code class="language-plaintext highlighter-rouge">info()</code>. pandas also calculates basic descriptive statistics and lets us see them via <code class="language-plaintext highlighter-rouge">describe()</code> method.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">df_weather</span><span class="p">.</span><span class="n">loc</span><span class="p">[:,</span> <span class="p">[</span><span class="sh">'</span><span class="s">Temperature (C)</span><span class="sh">'</span><span class="p">]].</span><span class="nf">describe</span><span class="p">()</span>
<span class="o">&gt;&gt;&gt;</span>         <span class="nc">Temperature </span><span class="p">(</span><span class="n">C</span><span class="p">)</span>
    <span class="n">count</span>      <span class="mf">96453.000000</span>
    <span class="n">mean</span>       <span class="mf">11.932678</span>
    <span class="n">std</span>        <span class="mf">9.551546</span>
    <span class="nb">min</span>        <span class="o">-</span><span class="mf">21.822222</span>
    <span class="mi">25</span><span class="o">%</span>        <span class="mf">4.688889</span>
    <span class="mi">50</span><span class="o">%</span>        <span class="mf">12.000000</span>
    <span class="mi">75</span><span class="o">%</span>        <span class="mf">18.838889</span>
    <span class="nb">max</span>        <span class="mf">39.905556</span>
</code></pre></div></div> <p>The numbers $25\%$, $50\%$, and $75\%$ are called <strong>k-th percentiles</strong> where $k$ is the percentage.</p> <ul> <li>$50\%$ is the median.</li> <li>$25\%$ means one fourth of the sorted dataset from minimum.</li> <li>$75\%$ means one fourth of the sorted dataset from maximum.</li> </ul> <p>Using percentiles, we can indicate if the distribution of a dataset is more left-skewed or right-skewed.</p> <p>:pushpin: <strong>Pro tip:</strong> pandas includes many other functions and methods that allows us to analyze how training models work for a dataset. This helps us know which model we should use for our datasets in certain cases.</p> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> What is the difference between <code>df.loc[:, 'Radio']</code> and <code>df.loc[:, ['Radio']]</code> in the indexing and slicing part above? <br/> <strong>Exercise 2:</strong> Write code to get the "Radio" column without using <code>loc</code>. <br/> <strong>Exercise 3:</strong> Write code to get rows 1-4 using the output of exercises 1 and 2. <br/> <strong>Exercise 4:</strong> Write code to drop "Radio" and "Sales" columns in exercise 1. <br/> <strong>Exercise 5:</strong> Write code to drop rows 1, 3, 6 in exercise 1 (before and after setting index). <br/> <strong>Exercise 6:</strong> For examples containing 'Temperature (C)', write code to add a 'Temperature (F)' column. <br/> <strong>Exercise 7:</strong> Using data in exercise 1, what is the output of <code>df.loc[3, 'Radio']</code>, <code>df.loc[3, 4]</code>, <code>df.loc[2::2]</code>, <code>df.drop('Radio')</code>, and <code>df.iloc[1:4, 1:5]</code>? </div> </details> <hr/> <h2 id="visualization"><strong>Visualization</strong></h2> <p>We can use <strong>seaborn</strong>, which is built on top of <strong>matplotlib</strong> to visualize data.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># set up seaborn
</span><span class="kn">import</span> <span class="n">seaborn</span> <span class="k">as</span> <span class="n">sns</span>
</code></pre></div></div> <p>We’ll use a famous Iris flower classification example here.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">df_iris</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nf">read_csv</span><span class="p">(</span><span class="sh">'</span><span class="s">iris.csv</span><span class="sh">'</span><span class="p">)</span>

<span class="c1"># graph two columns in one figure
</span><span class="n">sns</span><span class="p">.</span><span class="nf">jointplot</span><span class="p">(</span><span class="n">df_iris</span><span class="p">,</span> <span class="n">x</span><span class="o">=</span><span class="sh">'</span><span class="s">PetalLength</span><span class="sh">'</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="sh">'</span><span class="s">PetalWidth</span><span class="sh">'</span><span class="p">,</span> <span class="n">hue</span><span class="o">=</span><span class="sh">'</span><span class="s">Name</span><span class="sh">'</span><span class="p">)</span>
</code></pre></div></div> <p>This kind of plot is quite beneficial for both <em>classification and prediction problems</em> when we analyze to choose the right columns to compare.</p> <figure> <div style="display: flex; flex-wrap:wrap; column-gap: 20px; justify-content: center"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Cwednesday_visualization2_jointplot-480.webp 480w,/assets%5Caio2025%5CM03W01%5Cwednesday_visualization2_jointplot-800.webp 800w,/assets%5Caio2025%5CM03W01%5Cwednesday_visualization2_jointplot-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Cwednesday_visualization2_jointplot.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 320px; max-height: 450px; " alt="sepal length vs sepal width" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">Sepal Length vs Sepal Width</figcaption> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Cwednesday_visualization1_jointplot-480.webp 480w,/assets%5Caio2025%5CM03W01%5Cwednesday_visualization1_jointplot-800.webp 800w,/assets%5Caio2025%5CM03W01%5Cwednesday_visualization1_jointplot-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Cwednesday_visualization1_jointplot.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 320px; max-height: 450px; " alt="petal length vs petal width" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">Petal Length vs Petal Width</figcaption> </figure> </div> <figcaption style="text-align: center; width: 100%; font-size:0.875em; margin-top:-25px;"> <em>Figure 4.</em> Graphs plot three Iris flower types' features. In the second figure, Setosa is totally isolated from Versicolor and Verginica. We may use this to distinguish them. </figcaption> </figure> <p>We can use <code class="language-plaintext highlighter-rouge">pairplot()</code> to see all the pairs.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Cwednesday_visualization5_pairplot-480.webp 480w,/assets%5Caio2025%5CM03W01%5Cwednesday_visualization5_pairplot-800.webp 800w,/assets%5Caio2025%5CM03W01%5Cwednesday_visualization5_pairplot-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Cwednesday_visualization5_pairplot.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 600px; max-height: 600px; " alt="plots for every numerical pairs" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption"><em>Figure 5.</em>Plots for every numerical pairs</figcaption> </figure> <p>We can also check out the distribution of each type as follows. <strong>Boxplot</strong> gives the statistical structure of a feature where min, three primary percentiles, and max are shown from top to bottom by default.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># dotplot 
</span><span class="n">sns</span><span class="p">.</span><span class="nf">stripplot</span><span class="p">(</span><span class="n">df_iris</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="sh">'</span><span class="s">SepalLength</span><span class="sh">'</span><span class="p">,</span> <span class="n">x</span><span class="o">=</span><span class="sh">'</span><span class="s">Name</span><span class="sh">'</span><span class="p">,</span> <span class="n">hue</span><span class="o">=</span><span class="sh">'</span><span class="s">Name</span><span class="sh">'</span><span class="p">)</span>

<span class="c1"># boxplot
</span><span class="n">sns</span><span class="p">.</span><span class="nf">boxplot</span><span class="p">(</span><span class="n">df_iris</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="sh">'</span><span class="s">SepalLength</span><span class="sh">'</span><span class="p">,</span> <span class="n">x</span><span class="o">=</span><span class="sh">'</span><span class="s">Name</span><span class="sh">'</span><span class="p">,</span> <span class="n">hue</span><span class="o">=</span><span class="sh">'</span><span class="s">Name</span><span class="sh">'</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mf">0.3</span><span class="p">)</span>
</code></pre></div></div> <figure> <div style="display: flex; flex-wrap:wrap; column-gap: 20px; justify-content: center"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Cwednesday_visualization3_stripplot-480.webp 480w,/assets%5Caio2025%5CM03W01%5Cwednesday_visualization3_stripplot-800.webp 800w,/assets%5Caio2025%5CM03W01%5Cwednesday_visualization3_stripplot-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Cwednesday_visualization3_stripplot.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 320px; max-height: 450px; " alt="sepal length dotplot" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">Sepal Length dotplot</figcaption> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Cwednesday_visualization4_boxplot-480.webp 480w,/assets%5Caio2025%5CM03W01%5Cwednesday_visualization4_boxplot-800.webp 800w,/assets%5Caio2025%5CM03W01%5Cwednesday_visualization4_boxplot-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Cwednesday_visualization4_boxplot.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 320px; max-height: 450px; " alt="sepal length boxplot" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">Sepal Length boxplot</figcaption> </figure> </div> <figcaption style="text-align: center; width: 100%; font-size:0.875em; margin-top:-25px;"> <em>Figure 6.</em> Graphs plot three Iris flower types' features. In the second figure, Setosa is totally isolated from Versicolor and Verginica. We may use this to distinguish them. </figcaption> </figure> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> Write code to plot the rest for the Iris flower example give 5 columns as above. </div> </details> <hr/> <h2 id="speech_balloon-faq">:speech_balloon: <strong>FAQ</strong></h2> <p>Here are some good questions asked during class. The answers to these questions came from either some admin, TA, STA, or members of AIO2025.</p> <table> <thead> <tr> <th>Questions</th> <th>Answers</th> </tr> </thead> <tbody> <tr> <td>Is there a way to interpolate for one column using other columns?</td> <td>Yes. There are many ways, for example using decision tree, random forest.</td> </tr> <tr> <td>How do I know which method I should use to fill missing values?</td> <td>We try, particularly from the baseline method, interpolation, then improve it.</td> </tr> <tr> <td>How do I know if a dataset has noise?</td> <td>Ask experts in that field.</td> </tr> <tr> <td>After filling in missing values, data will be distorted. Is it okay?</td> <td>Yes. Actually we want to distort data properly since we care more about the data from the global and long-term point of view.</td> </tr> <tr> <td>If a feature X has 2 missing values belonging to 2 different classes, how should we go about filling in those values?</td> <td>You can fill them according to values in their own class or use other features to decide.</td> </tr> </tbody> </table> <p><br/></p> ]]></content><author><name></name></author><category term="AI"/><category term="coding"/><category term="AIO"/><category term="prob-stat"/><summary type="html"><![CDATA[pandas, series, dataframe, visualization, seaborn]]></summary></entry><entry><title type="html">Data Visualization and Analysis for AI 101 Warm-up</title><link href="https://huong-vo.github.io/blog/2025/data-visualization-analysis-pre101/" rel="alternate" type="text/html" title="Data Visualization and Analysis for AI 101 Warm-up"/><published>2025-08-04T00:00:00+00:00</published><updated>2025-08-04T00:00:00+00:00</updated><id>https://huong-vo.github.io/blog/2025/data-visualization-analysis-pre101</id><content type="html" xml:base="https://huong-vo.github.io/blog/2025/data-visualization-analysis-pre101/"><![CDATA[<p><strong>Acknowledgement:</strong> This post is based mainly on TA Nguyen Quoc Thai’s presentation and slides and partly on on TA, STA, and peers’ responses from the course AIO2025 offered by AI VIETNAM.</p> <p><strong>Relevance:</strong> The audience of this lecture were those who have read <a href="/blog/2025/math102/">Math for AI 102</a>, had some coding experience, and known a bit of AI knowledge. Absolute beginners are encouraged to read the material and repeatedly practice coding without AI assistance to strengthen their coding and code-reading skills. Note that only necessary pandas knowledge is introduced here.</p> <p>In today’s post, we’ll explore basic pandas using examples: Pokemon Data Analysis and Time Series Dataset.</p> <hr/> <h2 id="pandas"><strong>pandas</strong></h2> <h3 id="introduction"><strong>Introduction</strong></h3> <p><a href="https://en.wikipedia.org/wiki/Data_analysis">Data analysis</a> is a process of inspecting, cleansing, transforming, and modeling data with the goal of discovering useful information, informing conclusions, and supporting decision-making. That is, it includes visualization like charts, graphs, or maps based on which users can produce insights about a dataset.</p> <p><strong>pandas</strong> is a library built on NumPy written specifically for Python language. It is a data manipulation and analysis tool, providing simple and practical data structures ready for usage.</p> <p>Check out <a href="https://pandas.pydata.org/docs/">pandas documentation</a> for more packages. We can set up pandas and check its version as follows.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">!</span><span class="n">pip</span> <span class="n">install</span> <span class="n">pandas</span>

<span class="kn">import</span> <span class="n">pandas</span> <span class="k">as</span> <span class="n">pd</span>

<span class="n">pd</span><span class="p">.</span><span class="n">__version__</span>
<span class="o">&gt;&gt;&gt;</span> 
</code></pre></div></div> <p>There are three primary data structures in pandas, but one of them is barely used these days, so we’ll focus on the other two: <em>DataFrame</em> and <em>Series</em>. Since pandas is built on NumPy, its data structures have certain similarities with ndarray.</p> <h3 id="dataframe"><strong>DataFrame</strong></h3> <p>A <strong>DataFrame</strong> is a 2D labeled data structure in which data in columns can be of different types. Visually, it is like 2D tables with headers in Excel.</p> <p>Upon its creation, indices for rows are <em>automatically generated</em> by default. But we can certainly set up our own indices.</p> <p>We are allowed to create a dataframe from various data structures such as arrays, dictionaries, lists, zip() functions, or even an existing DataFrame. Take a look at an example below.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data</span> <span class="o">=</span> <span class="p">{</span>
    <span class="sh">'</span><span class="s">Name</span><span class="sh">'</span><span class="p">:</span> <span class="p">[</span><span class="sh">'</span><span class="s">Brucy</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Fiona</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Ziggy</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Ranger</span><span class="sh">'</span><span class="p">],</span>
    <span class="sh">'</span><span class="s">Gender</span><span class="sh">'</span><span class="p">:</span> <span class="p">[</span><span class="sh">'</span><span class="s">M</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">F</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">F</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">M</span><span class="sh">'</span><span class="p">]</span>
<span class="p">}</span>

<span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nc">DataFrame</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="n">df</span>

<span class="sh">'''</span><span class="s">
    Name    Gender
0   Brucy   M
1   Fiona   F
2   Ziggy   F
3   Ranger  M
</span><span class="sh">'''</span>
</code></pre></div></div> <h3 id="series"><strong>Series</strong></h3> <p>A <strong>Series</strong> is a 1D labeled array that can hold any data type. Upon its creation, indices for rows also are <em>automatically generated</em> by default. But we can explicitly name the indices. Since there is only one non-indexed column, the header row is not shown in the rendered table, but we can display it by converting the Series to a DataFrame.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">array</span><span class="p">([</span><span class="sh">'</span><span class="s">Brucy</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Fiona</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Ziggy</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Ranger</span><span class="sh">'</span><span class="p">])</span>

<span class="n">s</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nc">Series</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">name</span> <span class="o">=</span> <span class="sh">'</span><span class="s">Name</span><span class="sh">'</span><span class="p">)</span>
<span class="n">s</span>

<span class="sh">'''</span><span class="s">
0     Brucy
1     Fiona
2     Ziggy
3    Ranger
Name: Name, dtype: object
</span><span class="sh">'''</span>
</code></pre></div></div> <p>The definitions of both data structures infer that a DataFrame is <em>essentially</em> a combination of a Series sequence. Continue from the DataFrame example.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">name_series</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="sh">'</span><span class="s">Name</span><span class="sh">'</span><span class="p">]</span>
<span class="nf">type</span><span class="p">(</span><span class="n">name_series</span><span class="p">)</span>
<span class="o">&lt;</span><span class="k">class</span> <span class="err">'</span><span class="nc">pandas</span><span class="p">.</span><span class="n">core</span><span class="p">.</span><span class="n">series</span><span class="p">.</span><span class="n">Series</span><span class="sh">'</span><span class="s">&gt;

gender_series = df[</span><span class="sh">'</span><span class="n">Gender</span><span class="sh">'</span><span class="s">]
type(gender_series)
&lt;class </span><span class="sh">'</span><span class="n">pandas</span><span class="p">.</span><span class="n">core</span><span class="p">.</span><span class="n">series</span><span class="p">.</span><span class="n">Series</span><span class="sh">'</span><span class="s">&gt;

new_df = pd.concat([name_series, gender_series], axis=1)
type(new_df)
&lt;class </span><span class="sh">'</span><span class="n">pandas</span><span class="p">.</span><span class="n">core</span><span class="p">.</span><span class="n">frame</span><span class="p">.</span><span class="n">DataFrame</span><span class="sh">'</span><span class="s">&gt;
</span></code></pre></div></div> <p>Thus, we can also create a DataFrame from Series.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># create two Series
</span><span class="n">name_series</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nc">Series</span><span class="p">([</span><span class="sh">'</span><span class="s">Brucy</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Fiona</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Ziggy</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Ranger</span><span class="sh">'</span><span class="p">],</span> <span class="n">name</span><span class="o">=</span><span class="sh">'</span><span class="s">Name</span><span class="sh">'</span><span class="p">)</span>
<span class="n">gender_series</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nc">Series</span><span class="p">([</span><span class="sh">'</span><span class="s">M</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">F</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">F</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">M</span><span class="sh">'</span><span class="p">],</span> <span class="n">name</span><span class="o">=</span><span class="sh">'</span><span class="s">Gender</span><span class="sh">'</span><span class="p">)</span>

<span class="c1"># create DataFrame from Series
</span><span class="n">df_from_series</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nf">concat</span><span class="p">([</span><span class="n">name_series</span><span class="p">,</span> <span class="n">gender_series</span><span class="p">],</span> <span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="n">df_from_series</span>

<span class="sh">'''</span><span class="s">
    Name    Gender
0   Brucy   M
1   Fiona   F
2   Ziggy   F
3   Ranger  M
</span><span class="sh">'''</span>
</code></pre></div></div> <hr/> <p>In the next two sections, we’ll use a couple of hands-on examples to learn pandas step by step. These datasets are written in .csv files, which are quite common for storing data.</p> <h2 id="pokemon-data-analysis"><strong>Pokemon Data Analysis</strong></h2> <p><strong>Read and Save a CSV File</strong> <br/> If we print <code class="language-plaintext highlighter-rouge">data</code> out, we’ll see the information table and its numbers of rows and columns.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">DATASET_PATH</span> <span class="o">=</span> <span class="sh">'</span><span class="s">Pokemon.csv</span><span class="sh">'</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nf">read_csv</span><span class="p">(</span><span class="n">DATASET_PATH</span><span class="p">)</span>
</code></pre></div></div> <p>We can certainly save a dataset to a file under various format. The following is for a .csv file.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data</span><span class="p">.</span><span class="nf">to_csv</span><span class="p">(</span><span class="sh">'</span><span class="s">new_Pokemon.csv</span><span class="sh">'</span><span class="p">)</span>
</code></pre></div></div> <p><strong>Head/Tail Method</strong> <br/> With this method, we can check out how many first and last rows we want with some following syntax. By default, five rows at the top or at the bottom of the DataFrame will be shown when no argument is added.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data</span><span class="p">.</span><span class="nf">head</span><span class="p">()</span>
<span class="n">data</span><span class="p">.</span><span class="nf">head</span><span class="p">(</span><span class="n">n</span><span class="o">=</span><span class="mi">7</span><span class="p">)</span>
<span class="n">data</span><span class="p">.</span><span class="nf">head</span><span class="p">(</span><span class="mi">900</span><span class="p">)</span>
<span class="n">data</span><span class="p">.</span><span class="nf">tail</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
</code></pre></div></div> <p><strong>Rows</strong> <br/> Not only header/tail rows, we can read an equally-spaced sequence of rows by <em>slicing</em> a DataFrame using <code class="language-plaintext highlighter-rouge">[start:stop:step]</code>.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data</span><span class="p">[</span><span class="mi">2</span><span class="p">:</span><span class="mi">10</span><span class="p">:</span><span class="mi">3</span><span class="p">]</span>

<span class="sh">'''</span><span class="s">
    #   Name                        Type 1  Type 2  Total   HP  Attack  Defense Sp. Atk Sp. Def Speed   Generation  Legendary
2   3   Venusaur                    Grass   Poison  525     80  82      83      100     100     80      1           False
5   5   Charmeleon                  Fire    NaN     405     58  64      58      80      65      80      1           False
8   6   CharizardMega Charizard Y   Fire    Flying  634     78  104     78      159     115     100     1           False
</span><span class="sh">'''</span>
</code></pre></div></div> <p>However, we cannot do indexing to read one specific row. Instead, we can use <code class="language-plaintext highlighter-rouge">iloc</code>—<strong>index-based</strong> selecting method. We can treat a row index as an array index or a real row in a table. Thus, the datatypes of two ways are different—one is a Series and the other is a DataFrame.</p> <p>Only a snippet of the output is provided in the following code.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># this renders a Series
</span><span class="n">data</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">7</span><span class="p">]</span>

<span class="sh">'''</span><span class="s">
#                                     6
Name          CharizardMega Charizard X
Type 1                             Fire
</span><span class="gp">...</span>                                 <span class="bp">...</span>

<span class="s">Name: 7, dtype: object
</span><span class="sh">'''</span>
</code></pre></div></div> <p>A Series is like a 1D array, so we can get its elements by their indices or slicing. Or you can think of it as getting an element from a 2D array where the first argument is index/slice for rows and the second for columns.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">7</span><span class="p">,</span> <span class="mi">10</span><span class="p">]</span>

<span class="sh">'''</span><span class="s">
100
</span><span class="sh">'''</span>
</code></pre></div></div> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># this renders a DataFrame
</span><span class="n">data</span><span class="p">.</span><span class="n">iloc</span><span class="p">[[</span><span class="mi">7</span><span class="p">]]</span>

<span class="sh">'''</span><span class="s">
    #   Name                        Type 1  Type 2  Total   HP  Attack  Defense Sp. Atk Sp. Def SpeeD   Generation  Legendary
7   6   CharizardMega Charizard X   Fire    Dragon  634     78  130     111     130     85      100     1           False
</span><span class="sh">'''</span>
</code></pre></div></div> <p>Since it is a DataFrame, it can handle multiple rows and columns simultaneously. We can read more rows by adding more row indices to the list.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># this renders a DataFrame
</span><span class="n">data</span><span class="p">.</span><span class="n">iloc</span><span class="p">[[</span><span class="mi">7</span><span class="p">,</span> <span class="mi">10</span><span class="p">]]</span>

<span class="sh">'''</span><span class="s">
    #   Name	                    Type 1  Type 2  Total   HP  Attack  Defense Sp. Atk Sp. Def Speed   Generation  Legendary
7   6   CharizardMega Charizard X   Fire    Dragon  634	    78  130	    111	    130	    85	    100	    1	        False
10  8   Wartortle                   Water   NaN	    405	    59  63	    80	    65	    80	    58	    1	        False
</span><span class="sh">'''</span>
</code></pre></div></div> <p>If row indices are renamed to something esle, we can use <code class="language-plaintext highlighter-rouge">loc</code>—<strong>label-based</strong> selecting method to check out rows. Because of how it works, we must use a header or a list of headers rather than an index or slicing to get elements.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="mi">7</span><span class="p">,</span> <span class="sh">'</span><span class="s">Legendary</span><span class="sh">'</span><span class="p">]</span>

<span class="sh">'''</span><span class="s">
False
</span><span class="sh">'''</span>
</code></pre></div></div> <p>This is also very powerful since it can filtering rows. We’ll talk more about it later when we have sufficient tools.</p> <p><strong>Reset Index</strong> <br/> As we see above, indices of rows are kept the same as in the original dataset when extracted. This is inconvenient to work with just that part of the dataset. We can save it to a new variable and reset index. The original indices are kept and the new indices is added.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">new_slice</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="mi">7</span><span class="p">:</span><span class="mi">20</span><span class="p">:</span><span class="mi">3</span><span class="p">]</span>
<span class="n">new_slice</span><span class="p">.</span><span class="nf">reset_index</span><span class="p">()</span>

<span class="sh">'''</span><span class="s">
    index   #   Name                        Type 1  Type 2  Total   HP  Attack  Defense Sp. Atk Sp. Def Speed   Generation  Legendary
0   7       6   CharizardMega Charizard X   Fire    Dragon  634     78  130     111     130     85      100     1           False
1   10      8   Wartortle                   Water   NaN     405     59  63      80      65      80      58      1           False
2   13      10  Caterpie                    Bug     NaN     195     45  30      35      20      20      45      1           False
3   16      13  Weedle                      Bug     Poison  195     40  35      30      20      20      50      1           False
4   19      15  BeedrillMega Beedrill       Bug     Poison  495     65  150     40      15      80      145     1           False
</span><span class="sh">'''</span>
</code></pre></div></div> <p><strong>Headers</strong> <br/> We can see all the headers of columns in a dataset. They will</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data</span><span class="p">.</span><span class="n">columns</span>
<span class="o">&gt;&gt;&gt;</span> <span class="nc">Index</span><span class="p">([</span><span class="sh">'</span><span class="s">#</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Name</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Type 1</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Type 2</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Total</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">HP</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Attack</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Defense</span><span class="sh">'</span><span class="p">,</span>
           <span class="sh">'</span><span class="s">Sp. Atk</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Sp. Def</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Speed</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Generation</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Legendary</span><span class="sh">'</span><span class="p">],</span>
           <span class="n">dtype</span><span class="o">=</span><span class="sh">'</span><span class="s">object</span><span class="sh">'</span><span class="p">)</span>
</code></pre></div></div> <p><strong>Columns</strong> <br/> There are two simple ways to check out data values in particular columns. We can treat a header as an index of a list or a real header of a table. Like rows above, one result is a Series and the other is a DataFrame.</p> <p>Only a snippet of the output is provided in the following code. Check out the practice exercise to read more than one columns.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data</span><span class="p">[</span><span class="sh">'</span><span class="s">Name</span><span class="sh">'</span><span class="p">]</span>

<span class="n">data</span><span class="p">[[</span><span class="sh">'</span><span class="s">Name</span><span class="sh">'</span><span class="p">]]</span>
</code></pre></div></div> <p><strong>Row Filtering</strong> <br/> Like an array, we can filter rows via comparison operations. We can see whole rows or just a few columns of satisfied rows.</p> <p>First, let’s apply <code class="language-plaintext highlighter-rouge">loc</code>.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># only rows with data value being True in the Legendary column are selected
</span><span class="n">data</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">data</span><span class="p">[</span><span class="sh">'</span><span class="s">Legendary</span><span class="sh">'</span><span class="p">]</span> <span class="o">==</span> <span class="bp">True</span><span class="p">]</span>

<span class="c1"># only a column and rows with data value being True in the Legendary column are selected
</span><span class="n">data</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">data</span><span class="p">[</span><span class="sh">'</span><span class="s">Legendary</span><span class="sh">'</span><span class="p">]</span> <span class="o">==</span> <span class="bp">True</span><span class="p">,</span> <span class="sh">'</span><span class="s">Name</span><span class="sh">'</span><span class="p">]</span>

<span class="n">data</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">data</span><span class="p">[</span><span class="sh">'</span><span class="s">Legendary</span><span class="sh">'</span><span class="p">]</span> <span class="o">==</span> <span class="bp">True</span><span class="p">,</span> <span class="p">[</span><span class="sh">'</span><span class="s">Name</span><span class="sh">'</span><span class="p">]]</span>
</code></pre></div></div> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data</span><span class="p">.</span><span class="n">loc</span><span class="p">[(</span><span class="n">data</span><span class="p">[</span><span class="sh">'</span><span class="s">Type 1</span><span class="sh">'</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">Grass</span><span class="sh">'</span><span class="p">)</span> <span class="o">&amp;</span> <span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="sh">'</span><span class="s">Type 2</span><span class="sh">'</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">Poison</span><span class="sh">'</span><span class="p">)]</span>
</code></pre></div></div> <p>In addition, we can choose selective rows by <strong>Regex</strong> with package <code class="language-plaintext highlighter-rouge">import re</code>.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">re</span>

<span class="c1"># rows with name containing 'Mega'
</span><span class="n">data</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">data</span><span class="p">[</span><span class="sh">'</span><span class="s">Name</span><span class="sh">'</span><span class="p">].</span><span class="nb">str</span><span class="p">.</span><span class="nf">contains</span><span class="p">(</span><span class="sh">'</span><span class="s">Mega</span><span class="sh">'</span><span class="p">)]</span>

<span class="c1"># rows with type 1 being either 'Fire' or 'Grass'
</span><span class="n">data</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">data</span><span class="p">[</span><span class="sh">'</span><span class="s">Type 1</span><span class="sh">'</span><span class="p">].</span><span class="nb">str</span><span class="p">.</span><span class="nf">contains</span><span class="p">(</span><span class="sh">'</span><span class="s">Fire|Grass</span><span class="sh">'</span><span class="p">)]</span>
</code></pre></div></div> <p><strong>Describe Method</strong> <br/> pandas provides basic descriptive statistics on every numerical column of a dataset with a simple syntax <code class="language-plaintext highlighter-rouge">describe()</code>.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data</span><span class="p">.</span><span class="nf">describe</span><span class="p">()</span>
</code></pre></div></div> <p><strong>Arranging Order</strong> <br/> We can sort a dataset based on columns and the result is in an increasing order by default. We can add the <code class="language-plaintext highlighter-rouge">ascending=False</code> or <code class="language-plaintext highlighter-rouge">ascending=0</code> argument to reverse it. We use a list to sort multiple columns and various orders. This method does <em>not</em> affect the original data.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># sort by one column
</span><span class="n">data</span><span class="p">.</span><span class="nf">sort_values</span><span class="p">(</span><span class="sh">'</span><span class="s">Total</span><span class="sh">'</span><span class="p">)</span>

<span class="c1"># sort by multiple columns
</span><span class="n">data</span><span class="p">.</span><span class="nf">sort_values</span><span class="p">([</span><span class="sh">'</span><span class="s">Total</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Type 1</span><span class="sh">'</span><span class="p">],</span> <span class="n">ascending</span><span class="o">=</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">])</span>
</code></pre></div></div> <p>We can also arrange columns in a certain order. This produces a DataFrame of selected columns in the desired order. Thus, this method does <em>not</em> affect the original data.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data</span><span class="p">[[</span><span class="sh">'</span><span class="s">Type 1</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Name</span><span class="sh">'</span><span class="p">]]</span>
</code></pre></div></div> <p><strong>Update Data</strong> <br/> We can add or remove columns for a dataset. Use <code class="language-plaintext highlighter-rouge">drop()</code> for column removal; this does <em>not</em> affect the original dataset. On the other hand, adding a column updates the original dataset. If an existing column is modified, it will be updated rather than a new column is added.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># remove a column and store the new dataset to a new variable
</span><span class="n">new_data</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="nf">drop</span><span class="p">(</span><span class="n">columns</span><span class="o">=</span><span class="sh">'</span><span class="s">Total</span><span class="sh">'</span><span class="p">)</span>

<span class="c1"># add a column
# add two numerical columns
</span><span class="n">new_data</span><span class="p">[</span><span class="sh">'</span><span class="s">Attack + Defense</span><span class="sh">'</span><span class="p">]</span> <span class="o">=</span> <span class="n">new_data</span><span class="p">[</span><span class="sh">'</span><span class="s">Attack</span><span class="sh">'</span><span class="p">]</span> <span class="o">+</span> <span class="n">new_data</span><span class="p">[</span><span class="sh">'</span><span class="s">Defense</span><span class="sh">'</span><span class="p">]</span>
</code></pre></div></div> <p><strong>groupby Function</strong> <br/> We can group values of columns based on their types.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data</span><span class="p">.</span><span class="nf">groupby</span><span class="p">([</span><span class="sh">'</span><span class="s">Type 1</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Type 2</span><span class="sh">'</span><span class="p">]).</span><span class="nf">count</span><span class="p">()</span>

<span class="sh">'''</span><span class="s">
                    #   Name    Total   HP  Attack  Defense Sp. Atk Sp. Def Speed   Generation  Legendary
Type 1	Type 2											
Bug     Electric    2   2       2       2   2       2       2       2       2       2           2
        Fighting    2   2       2       2   2       2       2       2       2       2           2
</span><span class="gp">...</span>     <span class="p">...</span>         <span class="p">...</span> <span class="p">...</span>     <span class="p">...</span>     <span class="p">...</span> <span class="p">...</span>     <span class="p">...</span>     <span class="p">...</span>     <span class="p">...</span>     <span class="p">...</span>     <span class="p">...</span>         <span class="p">...</span>  

<span class="s">Water   Ice         3   3       3       3   3       3       3       3       3       3           3
        Poison      3   3       3       3   3       3       3       3       3       3           3
</span><span class="sh">'''</span>
</code></pre></div></div> <p>We can further this step by using an additional function to manipulate data. You can check out more functions as needed.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">data</span><span class="p">.</span><span class="nf">groupby</span><span class="p">([</span><span class="sh">'</span><span class="s">Type 1</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Type 2</span><span class="sh">'</span><span class="p">])[[</span><span class="sh">'</span><span class="s">HP</span><span class="sh">'</span><span class="p">]].</span><span class="nf">apply</span><span class="p">(</span>
    <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="p">.</span><span class="nf">sum</span><span class="p">()</span> <span class="o">/</span> <span class="nf">len</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="p">)</span>
</code></pre></div></div> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> What is the rendered output if the argument is greater than the total number of rows? An error or the entire table. <br/> <strong>Exercise 2:</strong> In the code blocks of "Columns", which one give a Series or a DataFrame? Which one can be used to read more than one column? <br/> <strong>Exercise 3:</strong> In the code blocks of "Columns", which one can be used to read more than one column? Explain why. What does the other result in? Explain why? <br/> <strong>Exercise 4:</strong> What is the output of <code>data.loc[7, 'Legendary', 'Total']</code> and <code>data.loc[7, ['Legendary', 'Total']]</code>? <br/> <strong>Exercise 5:</strong> In the code block of "Row Filtering", what can you do to show more than one column? </div> </details> <p><br/></p> <h2 id="time-series-dataset"><strong>Time Series Dataset</strong></h2> <p>A time series dataset including a continuous time period.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ts_df</span>

<span class="sh">'''</span><span class="s">
        Date        Daily minimum temperatures
0       1/1/1981    20.7
1       1/2/1981    17.9
2       1/3/1981    18.8
3       1/4/1981    14.6
4       1/5/1981    15.8
</span><span class="gp">...</span>	    <span class="p">...</span>	        <span class="bp">...</span>

<span class="s">3645    12/27/1990  14
3646    12/28/1990  13.6
3647    12/29/1990  13.5
3648    12/30/1990  15.7
3649    12/31/1990  13
</span><span class="sh">'''</span>
</code></pre></div></div> <p><strong>Time Format Conversion</strong> <br/> A dataset may have a date or time column, but their values might not be in the right format. pandas allows us to convert such data to time format which makes accessing a certain date/time row convenient. We can also get each part of a date/time; for example, day, month, or year.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># time format conversion
</span><span class="n">ts_df</span><span class="p">[</span><span class="sh">'</span><span class="s">Date</span><span class="sh">'</span><span class="p">]</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nf">to_datetime</span><span class="p">(</span><span class="n">ts_df</span><span class="p">[</span><span class="sh">'</span><span class="s">Date</span><span class="sh">'</span><span class="p">])</span>

<span class="c1"># set 'Date' column as an index column
</span><span class="n">ts_df</span> <span class="o">=</span> <span class="n">ts_df</span><span class="p">.</span><span class="nf">set_index</span><span class="p">(</span><span class="sh">'</span><span class="s">Date</span><span class="sh">'</span><span class="p">)</span>
</code></pre></div></div> <p>We can use the code below with some modification to create new meaningful columns for time.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># index_cols=0 takes the first column
# parse_dates reads data as time, not string
</span><span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nf">read_csv</span><span class="p">(</span><span class="sh">'</span><span class="s">timeseries_daily-minimum-temperatures.csv</span><span class="sh">'</span><span class="p">,</span> <span class="n">index_col</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">parse_dates</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>

<span class="n">df</span><span class="p">.</span><span class="n">index</span><span class="p">.</span><span class="n">year</span>
<span class="n">df</span><span class="p">.</span><span class="n">index</span><span class="p">.</span><span class="n">month</span>
<span class="n">df</span><span class="p">.</span><span class="n">index</span><span class="p">.</span><span class="nf">day_name</span><span class="p">()</span>
</code></pre></div></div> <p><strong>Time-based Indexing</strong> <br/> As mentioned, once we convert the ‘Date’ column to time format, we can retrieve rows via time-based indices.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># get rows from one date to another by slicing
</span><span class="n">ts_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="sh">'</span><span class="s">1987-08-29</span><span class="sh">'</span><span class="p">:</span><span class="sh">'</span><span class="s">1997-03-30</span><span class="sh">'</span><span class="p">]</span>

<span class="c1"># get ros in a specific month
</span><span class="n">ts_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="sh">'</span><span class="s">1990-01</span><span class="sh">'</span><span class="p">]</span>
</code></pre></div></div> <h3 id="time-series-data-visualization"><strong>Time Series Data Visualization</strong></h3> <p><strong>pandas</strong> <br/> In order to visualize data, values should be numeric for computers to understand. If there are values that cannot be converted into a number, for example, due to wrong format, we can either remove such a row or force the value to be fixed. Otherwise, an error is produced.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">col_to_plot</span> <span class="o">=</span> <span class="sh">'</span><span class="s">Daily minimum temperatures</span><span class="sh">'</span>

<span class="c1"># force wrong values to be fixed
</span><span class="n">ts_df</span><span class="p">[</span><span class="n">col_to_plot</span><span class="p">]</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nf">to_numeric</span><span class="p">(</span><span class="n">ts_df</span><span class="p">[</span><span class="n">col_to_plot</span><span class="p">],</span> <span class="n">errors</span><span class="o">=</span><span class="sh">'</span><span class="s">coerce</span><span class="sh">'</span><span class="p">)</span>

<span class="c1"># plot
</span><span class="n">ts_df</span><span class="p">[</span><span class="n">col_to_plot</span><span class="p">].</span><span class="nf">plot</span><span class="p">(</span>
    <span class="n">linewidth</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span>
    <span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span><span class="mi">4</span><span class="p">),</span>
    <span class="n">legend</span><span class="o">=</span><span class="bp">True</span>
<span class="p">)</span>

<span class="c1"># plot
</span><span class="n">ts_df</span><span class="p">[</span><span class="n">col_to_plot</span><span class="p">].</span><span class="nf">plot</span><span class="p">(</span>
    <span class="n">marker</span><span class="o">=</span><span class="sh">'</span><span class="s">.</span><span class="sh">'</span><span class="p">,</span>
    <span class="n">alpha</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span>
    <span class="n">linestyle</span><span class="o">=</span><span class="sh">'</span><span class="s">None</span><span class="sh">'</span><span class="p">,</span>
    <span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span><span class="mi">4</span><span class="p">),</span>
    <span class="n">legend</span><span class="o">=</span><span class="bp">True</span>
<span class="p">)</span>
</code></pre></div></div> <figure> <div style="display: flex; flex-wrap:wrap; column-gap: 20px; justify-content: center"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Ctuesday_daily_min_temp_line_plot-480.webp 480w,/assets%5Caio2025%5CM03W01%5Ctuesday_daily_min_temp_line_plot-800.webp 800w,/assets%5Caio2025%5CM03W01%5Ctuesday_daily_min_temp_line_plot-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Ctuesday_daily_min_temp_line_plot.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 320px; max-height: 450px; " alt="line plot" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">Line plot</figcaption> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Ctuesday_daily_min_temp_dot_plot-480.webp 480w,/assets%5Caio2025%5CM03W01%5Ctuesday_daily_min_temp_dot_plot-800.webp 800w,/assets%5Caio2025%5CM03W01%5Ctuesday_daily_min_temp_dot_plot-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Ctuesday_daily_min_temp_dot_plot.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 320px; max-height: 450px; " alt="dot plot" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">Dot plot</figcaption> </figure> </div> <figcaption style="text-align: center; width: 100%; font-size:0.875em; margin-top:-25px;"> <em>Figure 1:</em> Daily minimum temperatures </figcaption> </figure> <p><strong>Seasonality with seaborn</strong> <br/> When dealing with time series datasets, it’s important to understand data values according to their time blocks. This is called <em>seasonality</em>. Formally, <strong>seasonality</strong> is a characterisitc of a time series where data experiences roughly periodic changes recurring yearly.</p> <p><strong>seaborn</strong> is a Python data visualization library built on top of <strong>matplotlib</strong>.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="kn">import</span> <span class="n">seaborn</span> <span class="k">as</span> <span class="n">sns</span>

<span class="c1"># set figure size
</span><span class="n">sns</span><span class="p">.</span><span class="nf">set</span><span class="p">(</span><span class="n">rc</span><span class="o">=</span><span class="p">{</span><span class="sh">'</span><span class="s">figure.figsize</span><span class="sh">'</span><span class="p">:(</span><span class="mi">10</span><span class="p">,</span><span class="mi">4</span><span class="p">)})</span>
<span class="c1"># plt.figure(figsize=(10,4))
</span>
<span class="c1"># boxplot
</span><span class="n">sns</span><span class="p">.</span><span class="nf">boxplot</span><span class="p">(</span><span class="n">ts_df</span><span class="p">,</span> <span class="n">x</span><span class="o">=</span><span class="sh">'</span><span class="s">Month</span><span class="sh">'</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="n">col_to_plot</span><span class="p">)</span>
</code></pre></div></div> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Ctuesday_seasonal_min_temp_box_plot-480.webp 480w,/assets%5Caio2025%5CM03W01%5Ctuesday_seasonal_min_temp_box_plot-800.webp 800w,/assets%5Caio2025%5CM03W01%5Ctuesday_seasonal_min_temp_box_plot-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Ctuesday_seasonal_min_temp_box_plot.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 500px; max-height: 600px; " alt="seasonal minimum temperatures with boxplot" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption"><em>Figure 2:</em> Seasonal minimum temperatures with boxplot. The box has 3 lines associated with 25%, 50%, 75% and circles on top and bottom as outliers.</figcaption> </figure> <p><strong>Frequencies</strong> <br/> With time format, we can get a sequence of time periods based on their frequency and/or the number of periods. Frequency is day by default, but it can be, say hour.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">pd</span><span class="p">.</span><span class="nf">date_range</span><span class="p">(</span><span class="sh">'</span><span class="s">1997-03-30</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">1997-05-03</span><span class="sh">'</span><span class="p">)</span>

<span class="n">pd</span><span class="p">.</span><span class="nf">date_range</span><span class="p">(</span><span class="sh">'</span><span class="s">1997-03-30</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">1997-05-03</span><span class="sh">'</span><span class="p">,</span> <span class="n">freq</span><span class="o">=</span><span class="sh">'</span><span class="s">h</span><span class="sh">'</span><span class="p">)</span>

<span class="n">pd</span><span class="p">.</span><span class="nf">date_range</span><span class="p">(</span><span class="sh">'</span><span class="s">1997-03-30</span><span class="sh">'</span><span class="p">,</span> <span class="n">periods</span><span class="o">=</span><span class="mi">8</span><span class="p">,</span> <span class="n">freq</span><span class="o">=</span><span class="sh">'</span><span class="s">h</span><span class="sh">'</span><span class="p">)</span>
</code></pre></div></div> <p>Assume that we have only 3 random time series data points. But we want to convert data to daily frequency.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># convert string to time format
</span><span class="n">times_sample</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nf">to_datetime</span><span class="p">([</span><span class="sh">'</span><span class="s">1981-01-01</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">1981-01-04</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">1981-01-08</span><span class="sh">'</span><span class="p">])</span>

<span class="c1"># make a DataFrame to manipulate
</span><span class="n">consum_sample</span> <span class="o">=</span> <span class="n">ts_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">times_sample</span><span class="p">,</span> <span class="p">[</span><span class="sh">'</span><span class="s">Daily minimum temperatures</span><span class="sh">'</span><span class="p">]].</span><span class="nf">copy</span><span class="p">()</span>
<span class="n">consum_sample</span>

<span class="c1"># convert data to daily frequency
# new rows has no data
</span><span class="n">consum_freq</span> <span class="o">=</span> <span class="n">consum_sample</span><span class="p">.</span><span class="nf">asfreq</span><span class="p">(</span><span class="sh">'</span><span class="s">D</span><span class="sh">'</span><span class="p">)</span>
<span class="n">consum_freq</span>

<span class="c1"># create a column with missings to be forward filled
# forward fill is with 'before' data
# backward fill is with 'after' data
</span><span class="n">consum_freq</span><span class="p">[</span><span class="sh">'</span><span class="s">Daily minimum temperatures - Forward Fill</span><span class="sh">'</span><span class="p">]</span> <span class="o">=</span> <span class="n">consum_sample</span><span class="p">.</span><span class="nf">asfreq</span><span class="p">(</span>
    <span class="sh">'</span><span class="s">D</span><span class="sh">'</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="sh">'</span><span class="s">ffill</span><span class="sh">'</span>
<span class="p">)</span>
<span class="n">consum_freq</span>

<span class="sh">'''</span><span class="s">
            Daily minimum temperatures
1981-01-01  20.7
1981-01-04  14.6
1981-01-08  17.4


            Daily minimum temperatures
1981-01-01  20.7
1981-01-02  NaN
1981-01-03  NaN
1981-01-04  14.6
1981-01-05  NaN
1981-01-06  NaN
1981-01-07  NaN
1981-01-08  17.4

            Daily minimum temperatures  Daily minimum temperatures - Forward Fill
1981-01-01  20.7                        20.7
1981-01-02  NaN                         20.7
1981-01-03  NaN                         20.7
1981-01-04  14.6                        14.6
1981-01-05  NaN                         14.6
1981-01-06  NaN                         14.6
1981-01-07  NaN                         14.6
1981-01-08  17.4                        17.4
</span><span class="sh">'''</span>
</code></pre></div></div> <p><strong>Resampling</strong> <br/> Resampling is a technique used when values in each data block are close with each other. For example, temperatures at 11am, 12am, and 1pm are 99, 100, 101 degree Fahrenheit. We can somehow take only one value to represent for these data points to save resources.</p> <ul> <li>Find a common feature of values and use it to calculate a common number. Take mean or median for example.</li> <li>If there is no common feature, use one of the values.</li> </ul> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">col_to_resample</span> <span class="o">=</span> <span class="sh">'</span><span class="s">Daily minimum temperatures</span><span class="sh">'</span>

<span class="c1"># take the mean of temperatures by week
</span><span class="n">ts_weekly_mean</span> <span class="o">=</span> <span class="n">ts_df</span><span class="p">[[</span><span class="n">col_to_resample</span><span class="p">]].</span><span class="nf">resample</span><span class="p">(</span><span class="sh">'</span><span class="s">W</span><span class="sh">'</span><span class="p">).</span><span class="nf">mean</span><span class="p">()</span>
<span class="n">ts_weekly_mean</span><span class="p">.</span><span class="nf">head</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>

<span class="sh">'''</span><span class="s">
            Daily minimum temperatures
Date	
1981-01-04  18.000000
1981-01-11  17.542857
1981-01-18  20.371429
</span><span class="sh">'''</span>
</code></pre></div></div> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM03W01%5Ctuesday_daily_min_weekly_mean_temp_plot-480.webp 480w,/assets%5Caio2025%5CM03W01%5Ctuesday_daily_min_weekly_mean_temp_plot-800.webp 800w,/assets%5Caio2025%5CM03W01%5Ctuesday_daily_min_weekly_mean_temp_plot-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM03W01%5Ctuesday_daily_min_weekly_mean_temp_plot.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 500px; max-height: 600px; " alt="comparison of Daily and Weekly Mean Minimum Temperatures in 1981." data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption"><em>Figure 3:</em> Comparison of Daily and Weekly Mean Minimum Temperatures in 1981 <br/> This plot shows daily minimum temperature values (blue dots and lines) alongside their weekly mean resampling (orange dots and lines) for the period January to December 1981. The weekly mean resampling smooths out short-term fluctuations to reveal broader temperature trends.</figcaption> </figure> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># plot daily and weekly resampling for a period
</span><span class="n">start</span><span class="p">,</span> <span class="n">end</span> <span class="o">=</span> <span class="sh">'</span><span class="s">1981-01</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">1981-12</span><span class="sh">'</span>

<span class="c1"># create the plot
</span><span class="n">fig</span><span class="p">,</span> <span class="n">ax</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="nf">subplots</span><span class="p">()</span>

<span class="n">ax</span><span class="p">.</span><span class="nf">plot</span><span class="p">(</span>
    <span class="n">ts_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">start</span><span class="p">:</span> <span class="n">end</span><span class="p">,</span> <span class="n">col_to_resample</span><span class="p">],</span>
    <span class="n">marker</span><span class="o">=</span><span class="sh">'</span><span class="s">.</span><span class="sh">'</span><span class="p">,</span>
    <span class="n">linestyle</span><span class="o">=</span><span class="sh">'</span><span class="s">-</span><span class="sh">'</span><span class="p">,</span>
    <span class="n">linewidth</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span>
    <span class="n">label</span><span class="o">=</span><span class="sh">'</span><span class="s">Daily Minimum Temperatures</span><span class="sh">'</span>
<span class="p">)</span>

<span class="n">ax</span><span class="p">.</span><span class="nf">plot</span><span class="p">(</span>
    <span class="n">ts_weekly_mean</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">start</span><span class="p">:</span> <span class="n">end</span><span class="p">,</span> <span class="n">col_to_resample</span><span class="p">],</span>
    <span class="n">marker</span><span class="o">=</span><span class="sh">'</span><span class="s">o</span><span class="sh">'</span><span class="p">,</span>
    <span class="n">linestyle</span><span class="o">=</span><span class="sh">'</span><span class="s">-</span><span class="sh">'</span><span class="p">,</span>
    <span class="n">label</span><span class="o">=</span><span class="sh">'</span><span class="s">Weekly Mean Resampling</span><span class="sh">'</span>
<span class="p">)</span>

<span class="n">ax</span><span class="p">.</span><span class="nf">set_ylabel</span><span class="p">(</span><span class="sh">'</span><span class="s">Temperature</span><span class="sh">'</span><span class="p">)</span>
<span class="n">ax</span><span class="p">.</span><span class="nf">legend</span><span class="p">()</span>
</code></pre></div></div> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> Given the file's name is 'timeseries_daily-minimum-temperatures.csv', write code to read this dataset <br/> <strong>Exercise 2:</strong> Write more code to add 'Year', 'Month', and 'Day' columns to the dataset. </div> </details> <hr/> <h2 id="speech_balloon-faq">:speech_balloon: <strong>FAQ</strong></h2> <p>Here are some good questions asked during class. The answers to these questions came from either some admin, TA, STA, or members of AIO2025.</p> <table> <thead> <tr> <th>Questions</th> <th>Answers</th> </tr> </thead> <tbody> <tr> <td>If a dataset has more than one location, how can I visualize it?</td> <td>You should visualization according to each location. The dataset should have locations’ geographic coordinates.</td> </tr> </tbody> </table> <p><br/></p> ]]></content><author><name></name></author><category term="AI"/><category term="coding"/><category term="AIO"/><category term="prob-stat"/><summary type="html"><![CDATA[pandas, series, dataframe, visualization, seaborn]]></summary></entry><entry><title type="html">Math for AI 106</title><link href="https://huong-vo.github.io/blog/2025/math106/" rel="alternate" type="text/html" title="Math for AI 106"/><published>2025-07-18T00:00:00+00:00</published><updated>2025-07-18T00:00:00+00:00</updated><id>https://huong-vo.github.io/blog/2025/math106</id><content type="html" xml:base="https://huong-vo.github.io/blog/2025/math106/"><![CDATA[<p><strong>Acknowledgement:</strong> This post is based mainly on Dr. Vinh-Dinh Nguyen’s presentation and slides and partly on on TA, STA, and peers’ responses from the course AIO2025 offered by AI VIETNAM.</p> <p><strong>Relevance:</strong> The audience of this lecture were those who have read <a href="/blog/2025/math105/">Math for AI 105</a> or been familiar with probability and statistics. Note that only basic probability material is introduced here; we’ll pick up specific concepts when in need.</p> <p>In today’s post, we’ll delve into some more descriptive statistics. We’ll also discuss some applications in AI including template matching.</p> <hr/> <h2 id="measure-of-central-tendency"><strong>Measure of Central Tendency</strong></h2> <p>We previously talked about <strong>measure of central tendency</strong> in general and only briefly mentioned <em>population</em> and <em>sample</em>. We’ll discuss more in detail to distinguish the two concepts.</p> <h3 id="population-vs-sample-mean-variance-and-standard-deviation"><strong>Population vs Sample: Mean, Variance, and Standard Deviation</strong></h3> <table class="language-python"> <tr> <th></th> <th>Population</th> <th>Sample</th> </tr> <tr> <th scope="row">Mean</th> <td>$$\mu = \frac{1}{N} \sum_{k=1}^{N} x_k$$</td> <td>$$\bar{x} = \frac{1}{n} \sum_{k=1}^{n} x_k$$</td> </tr> <tr> <th scope="row">Variance</th> <td>$$\sigma^2 = var(X) = \frac{1}{N} \sum_{k=1}^{N} (x_k - \mu)^2$$</td> <td>$$s^2 = var(X) = \frac{1}{n - 1} \sum_{k=1}^{n} (x_k - \bar{x})^2$$</td> </tr> <tr> <th scope="row">Standard Deviation</th> <td>$$\sigma = \sqrt{var(X)}$$</td> <td>$$\sigma = \sqrt{var(X)}$$</td> </tr> <tr> <th scope="row">Notes</th> <td> <ul> <li>Rarely collect and measure all the data due to the need of effort, money, and time. </li> </ul> </td> <td> <ul> <li>Estimate the population mean by using a small sample. </li> </ul> </td> </tr> </table> <p><br/></p> <p><strong>Why not $n$?</strong> <br/> The sample variance calculated by dividing by $n$ is often referred to as the <strong>biased/naive sample variance</strong>. These name refer to a major problem it causes.</p> <p>The problem is that we would <em>consistently underestimate</em> the variance around the population mean. This happens because, <em>on average</em> across all possible samples drawn from the population, the naive sample variance is <em>smaller</em> than the population variance. That is, there is a disparity between the two</p> \[\begin{align} \label{naive-inequality-problem} E \left( \frac{1}{n} \sum_{k=1}^{n} (x_k - \bar{x})^2 \right) &lt; \frac{1}{N} \sum_{k=1}^{N} (x_k - \mu)^2. \end{align}\] <ol> <li>Let’s first understand what each side of this equality means. <ul> <li>The <em>right-hand side</em> is simply the population variance.</li> </ul> \[\frac{1}{N} \sum (x_k - \mu)^2 = \sigma^2\] <ul> <li>The <em>left-hand side</em> is the expected value of all possible naive sample variances, particularly the mean in this case. We have the left-hand side can be rewritten as</li> </ul> \[E \left( \frac{1}{n} \sum_{k=1}^{n} (x_k - \bar{x})^2 \right) = E(\tilde{s}^2).\] <p>Here, we are <em>not</em> just comparing a single sample’s naive variance. <em>Instead</em>, we want to analyze the relation between the naive sample variance and the entire population variance overall, that is, from a global point of view. Our <strong>goal</strong> is to drag the sample variance to be close to the population variance as much as possible.</p> </li> <li>Before working out the detail, let’t see how this idea might work in the case of a single sample’s naive variance and why we need the average. <ul> <li> <p><em>Claim:</em> \(\sum_{i=1}^{n} (x_k - \bar{x})^2 &lt; \sum_{i=1}^{N} (x_k - \mu)^2 \text{ for any } \bar{x}\).</p> </li> <li> <p><em>Proof</em>: According the claim, we want to minimize the sum of squared deviations. The common method is to figure out the absolute minimum, in particular local minimum in this case.</p> <p>Consider $f(a) = \sum_{k=1}^{n} (x_k - z)^2$. Differentiating the function, we have</p> \[\begin{align*} \frac{df}{dz} = \frac{d}{dz} \sum_{k=1}^{n} (x_k - z)^2 = -2 \sum_{k=1}^{n} (x_k - z). \end{align*}\] <p>The local minimum of the function is at</p> \[\begin{align*} &amp;-2 \sum_{k=1}^{n} (x_k - z) = 0 \Rightarrow \sum_{k=1}^{n} (x_k - z) = 0 \\ &amp;\Rightarrow \sum_{k=1}^{n} x_k = \sum_{k=1}^{n} z = nz \Rightarrow z = \frac{\sum_{k=1}^{n} x_k}{n} = \bar{x}. \end{align*}\] <p>This proves our claim, which suggests there <em>might</em> be a <em>bias</em> if we divide by $n$. So the denominator <strong>cannot be $n$</strong>.</p> <p>The graphs below display the idea using different sample sizes. As the sample size increases to the population size, the sample mean will get closer to the population mean.</p> </li> </ul> </li> </ol> <figure> <div style="display: flex; gap: 20px; justify-content: center"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM02W03%5Cfriday_why_not_n_single_1-480.webp 480w,/assets%5Caio2025%5CM02W03%5Cfriday_why_not_n_single_1-800.webp 800w,/assets%5Caio2025%5CM02W03%5Cfriday_why_not_n_single_1-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM02W03%5Cfriday_why_not_n_single_1.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" alt="sample size=5" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">sample size = 5</figcaption> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM02W03%5Cfriday_why_not_n_single_2-480.webp 480w,/assets%5Caio2025%5CM02W03%5Cfriday_why_not_n_single_2-800.webp 800w,/assets%5Caio2025%5CM02W03%5Cfriday_why_not_n_single_2-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM02W03%5Cfriday_why_not_n_single_2.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" alt="sample size=10" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">sample size = 10</figcaption> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM02W03%5Cfriday_why_not_n_single_3-480.webp 480w,/assets%5Caio2025%5CM02W03%5Cfriday_why_not_n_single_3-800.webp 800w,/assets%5Caio2025%5CM02W03%5Cfriday_why_not_n_single_3-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM02W03%5Cfriday_why_not_n_single_3.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" alt="sample size=70" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">sample size = 70</figcaption> </figure> </div> <figcaption style="text-align: center; width: 100%; font-size:0.875em; margin-top:-25px;"> <em>Figure 1.</em> Sum of square errors at sample mean and population mean </figcaption> </figure> <p>We cannot conclude the claim for the variance since the denominators are different. However, we know that the denominator of the naive sample variance should be smaller. It can be $n - 1$, $n - 2$, $n - 3$, and so on.</p> <p><strong>Why not $n - 2$, $n - 3$, and so on?</strong> <br/></p> <ol> <li> <p><em>Note:</em> This is a proof of our main claim. You can read the next part if you think it may be pedantic.</p> <p>Since we want to compare with the population variance, we want to involve $\mu$ in the left side of \eqref{naive-inequality-problem}.</p> \[\begin{align} (x_k - \bar{x})^2 &amp;= ((x_k - \mu) - (\bar{x} - \mu))^2 \nonumber \\ &amp;= (x_k - \mu)^2 - 2(x_k - \mu)(\bar{x} - \mu) + (\bar{x} - \mu)^2. \label{bar_x-to-mu} \end{align}\] <p>Using \eqref{bar_x-to-mu}, we have</p> \[\begin{align} \sum (x_k - \bar{x})^2 &amp;= \sum (x_k - \mu)^2 - \sum 2(x_k - \mu)(\bar{x} - \mu) + \sum (\bar{x} - \mu)^2 \nonumber \\ &amp;= \sum (x_k - \mu)^2 - 2(\bar{x} - \mu) \sum (x_k - \mu) + n (\bar{x} - \mu)^2. \label{sum-bar_x-to-mu} \end{align}\] <p>We know that</p> \[\begin{align} \label{x_k-mu} \sum (x_k - \mu) = \sum x_k - \sum \mu = n \bar{x} - n \mu = n (\bar{x} - \mu) \end{align}\] <p>Substitute \eqref{x_k-mu} into \eqref{sum-bar_x-to-mu}. We have</p> \[\begin{align} \sum (x_k - \bar{x})^2 &amp;= \sum (x_k - \mu)^2 - 2(\bar{x} - \mu) n (\bar{x} - \mu) + n (\bar{x} - \mu)^2 \nonumber \\ &amp;= \sum (x_k - \mu)^2 - 2n (\bar{x} - \mu)^2 + n (\bar{x} - \mu)^2 \nonumber \\ &amp;= \sum (x_k - \mu)^2 - n (\bar{x} - \mu)^2. \label{result-bar_x-to-mu} \end{align}\] <p>Substituting \eqref{result-bar_x-to-mu} into the left side of \eqref{naive-inequality-problem}, we have</p> \[\begin{align} E \left( \frac{1}{n} \sum (x_k - \bar{x})^2 \right) &amp;= E \left(\frac{1}{n} \left[\sum (x_k - \mu)^2 - n (\bar{x} - \mu)^2 \right] \right) \nonumber \\ &amp;= E \left(\frac{1}{n} \left[\sum (x_k - \mu)^2 \right] \right) - E \left(\frac{1}{n} n (\bar{x} - \mu)^2 \right) \nonumber \\ &amp;= \frac{1}{n} \sum [E ((x_k - \mu)^2)] - E ((\bar{x} - \mu)^2) \nonumber \\ &amp;= \frac{1}{n} n [E ((x_k - \mu)^2)] - E ((\bar{x} - \mu)^2) \nonumber \\ &amp;= [E ((x_k - \mu)^2)] - E ((\bar{x} - \mu)^2) \label{expectation-naive-sample-variance} \end{align}\] <p>Assume all the samples are <a href="https://en.wikipedia.org/wiki/Variance?utm_source=chatgpt.com#Population_variance">independent and identically distributed</a>—having the same variance $\sigma^2$. By definition of the general variance and <a href="https://en.wikipedia.org/wiki/Variance?utm_source=chatgpt.com">linear combination rule</a>, we have</p> \[\begin{align} \label{same-variance} E ((x_k - \mu)^2) = var(x_k) = \sigma^2 \end{align}\] <p>and</p> \[\begin{align} E ((\bar{x} - \mu)^2) &amp;= var(\bar{X}) = var \left(\frac{1}{n} \sum x_k \right) = \frac{1}{n^2} \sum var (x_k) \nonumber \\ &amp;= \frac{1}{n^2} \sum \sigma^2 = \frac{1}{n^2} n \sigma^2 = \frac{\sigma^2}{n}. \label{variance-of-sample-mean} \end{align}\] <p>Replacing \eqref{same-variance} and \eqref{variance-of-sample-mean} into \eqref{expectation-naive-sample-variance}, we have</p> \[\begin{align} \label{result-expectation-naive-sample-variance} E(\tilde{s}^2) = E ((\bar{x} - \mu)^2) = \sigma^2 - \frac{\sigma^2}{n} = \frac{n - 1}{n} \sigma^2. \end{align}\] <p>Thus, \eqref{result-expectation-naive-sample-variance} indicates that $\tilde{s}^2$ gives an estimate $\frac{n - 1}{n} \sigma^2$ of the population variance $\sigma^2$. This estimation contributes to the bias or difference by a factor of $\frac{n - 1}{n}$ between the two variances claimed in \eqref{naive-inequality-problem}.</p> </li> <li> <p>To <em>correct</em> this bias, we need to eliminate that exact factor by multiplying by its reciprocal. The result after correction is called <strong>(unbiased) sample variance</strong>, denoted $s^2$ as in the formula.</p> \[s^2 = \frac{n}{n - 1} \tilde{s}^2 = \frac{n}{n - 1} \left( \frac{1}{n} \sum_{k=1}^{n} (x_k - \bar{x})^2 \right) = \frac{1}{n - 1} \sum_{k=1}^{n} (x_k - \bar{x})^2\] </li> </ol> <h3 id="degrees-of-freedom"><strong>Degrees of Freedom</strong></h3> <p>In statistics, the <strong>degrees of freedom</strong> specify the number of independent variables used in the calculation of <strong>statstic</strong>—any quantity computed from values in a sample. In laymen terms, it is the number of information pieces required to know the rest of a sample which is <em>not</em> free to vary.</p> <p>Think about $y = a_1x_1 + \cdots a_nx_n$. This equation involves $n$ independent variables, say $x_1, \cdots, x_n$, since the value of $y$ depends on them and can be inferred knowing their values. So the degree of freedom is n.</p> <p>Let’s say if you toss a coin, there are two possible outcomes: head and tail. If you say it is not a tail, then it must be a head. This means head can be derived from the information on tail—if tail is independent, head is not. So there is 1 degree of freedom. The same goes for doing this experiment many times.</p> <p>Another example is that if we know the sample mean and all values but one in a sample of size $n$. The unknown is completely determined by the $n - 1$ known values because the mean must stay fixed. So the sample has $n - 1$ degrees of freedom. This is another reason why the <strong>unbiased estimator of variance</strong> divides by $n - 1$ as the constraints are all we need to control the sample space.</p> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> If I toss a coin 100 times and say I've got 30 tails. What is the degree of freedom? <br/> <strong>Exercise 2:</strong> If you are at a traffic light, call me at home, and say the light is not yellow or red. What is the degree of freedom? <br/> <strong>Exercise 3:</strong> Write a function to compute covariance in two ways (definition and NumPy package) and test with X = [7, 18, 29, 2, 10, 9, 9] and Y = [1, 6, 12, 8, 6, 21, 10]. <br/> <strong>Exercise 4:</strong> Which one is correct? <ol> <li> Sample mean always equals population mean. </li> <li> Sample mean approaches population mean as sample size increases. </li> <li> Sample variance is the same as population variance no matter how big or small the sample is. </li> <li> None is correct. </li> </ol> <strong>Exercise 5:</strong> Why is the variance of a sample usually smaller than the population variance? </div> </details> <hr/> <h2 id="measure-of-variability"><strong>Measure of Variability</strong></h2> <h3 id="covariance"><strong>Covariance</strong></h3> <p>In the previous post, we talked about <em>variance</em>, which shows how dispersed the data values are from the center (mean). That’s an individual measurement.</p> <p>If we want to understand the relationship between more than one single set of data, we need <em>covariance</em>. <strong>Covariance</strong> measures the linear relationship of two random variables, that is, how they change together.</p> <table class="language-python"> <tr> <th></th> <th>Population</th> <th>Sample</th> </tr> <tr> <th scope="row">Formula</th> <td>$$cov(X, Y) = \frac{\sum^{n} (x_k - \bar{x})(y_k - \bar{y})}{n}$$</td> <td>$$cov(X, Y) = \frac{\sum^{n} (x_k - \bar{x})(y_k - \bar{y})}{n - 1}$$</td> </tr> <tr> <th scope="row">Properties</th> <td colspan="2"> <ul> <li> $cov(X, Y) \in (-\infty, \infty)$. </li> <li> If it is positive covariance or $cov(X, Y) &gt; 0$, then $X$ <em>increases</em> implies $Y$'s <em>rise</em> on average, thus positive trending line. </li> <li> If it is negative covariance or $cov(X, Y) &lt; 0$, then $X$ <em>reduces</em> implies $Y$'s <em>decrease</em> on average, thus, negative trending line. </li> <li> If it is zero covariance or $cov(X, Y) = 0$, then there is <em>no</em> linear correlation between two random variables. </li> </ul> </td> </tr> <tr> <th scope="row">Drawbacks</th> <td colspan="2"> <ul> <li> It is sensitive to scaled data, which makes it hard to interpret. </li> <li> It only indicates the rough direction of two variables. </li> <li> It does not interpret how strong two variables are linked together or how relatively close data is to the trending line. </li> <li> It does not tell us how steep the slope of the trending line is. </li> </ul> </td> </tr> </table> <p><br/></p> <p><del>DEOMONSTRAION</del></p> <p>Even though covariance have quite a few disadvantages, it is a major computational stepping stone for the development of advanced statistical methods like <em>correlation coefficient</em> or <em>principal component analysis</em>.</p> <h3 id="correlation"><strong>Correlation</strong></h3> <p><strong>Correlation</strong> measures the strength and direction of a linear relationship between two variables. Basically, it describes how two variables are linked or associated with each other. Correlation does <em>not</em> mean one variable causes the other; there could be a third variable influencing both of them and thus this co-relationship.</p> <p>Correlation can be expressed by either a scatter plot or a <strong>correlation coefficient</strong>. The table below shows characteristics of <strong>Pearson’s correlation coefficient</strong>. We’ll talk more about it in the next part as well.</p> <table class="language-python"> <tr> <th></th> <th>Positive</th> <th>Negative</th> </tr> <tr> <th scope="row">Properties</th> <td colspan="2"> <ul> <li> $corr(X, Y) \in [-1, 1]$. </li> <li> It is <strong>insensitive</strong> to scaled data. </li> <li> It can tell us how strong two variables are associated with each other. </li> <li> It may help suggest a predicted range for input </li> </ul> </td> </tr> <tr> <th scope="row">Description</th> <td> <ul> <li> Positive correlation or $corr(X, Y) &gt; 0$. </li> <li> As $X$ increases, $Y$'s rise on average. </li> </ul> </td> <td> <ul> <li> Negative covariance or $corr(X, Y) &lt; 0$. </li> <li> As $X$ reduces, $Y$'s decrease on average. </li> </ul> </td> </tr> <tr> <th scope="row">Perfect Relationship</th> <td> <ul> <li> $corr(X, Y) = 1$. </li> <li> Values of $X$ and $Y$ form a straight line with a positive slope. </li> </ul> </td> <td> <ul> <li> $corr(X, Y) = -1$. </li> <li> Values of $X$ and $Y$ form a straight line with a negative slope. </li> </ul> </td> </tr> <tr> <th scope="row">Stronger Relationship</th> <td> <ul> <li> $corr(X, Y)$ is close to $1$. </li> <li> Values of $X$ and $Y$ lie closely to a straight line. </li> </ul> </td> <td> <ul> <li> $corr(X, Y)$ is close to $-1$. </li> <li> Values of $X$ and $Y$ lie closely to a straight line. </li> </ul> </td> </tr> <tr> <th scope="row">Weaker Relationship</th> <td> <ul> <li> $corr(X, Y)$ is not close to $1$. </li> <li> Values of $X$ and $Y$ are distributed around but further away from a straight line. </li> </ul> </td> <td> <ul> <li> $corr(X, Y)$ is not close to $-1$. </li> <li> Values of $X$ and $Y$ are distributed around but further away from a straight line. </li> </ul> </td> </tr> <tr> <th scope="row">No Relationship</th> <td colspan="2"> <ul> <li> $corr(X, Y) = 0$. </li> <li> Values scatter without a clear linear pattern. </li> </ul> </td> </tr> </table> <p><br/></p> <p><del>DEOMONSTRAION</del></p> <p><strong>Correlation vs Regression</strong> <br/> Consider $y = ax + b$. In this equation, $x$ is an independent variable while $y$ is dependent, that is, $y$ is affected/determined by $x$. This idea describe regression of two variables. Formally, <strong>regression</strong> describes a functional relationship between two variables—how one is dependent of the other. With such a property, we can use it to estimate the unknowns and make predictions based on assumptions.</p> <ul> <li>Correlation <em>may</em> imply regression since we can model a regression line but it might not be a good one.</li> <li>Regression <em>can</em> indicate correlation since regression how much one variable depends on the other.</li> </ul> <p><strong>Correlation vs Causation</strong> <br/> <strong>Causation</strong> indicates the cause and effect relationship between two variables.</p> <ul> <li>Correlation does <em>not</em> imply causation.</li> <li>Causation <em>may</em> imply correlation since that one variable causes the other means we can expect some correlation. Due to various factors, the correlation might not be clear.</li> </ul> <p>The figure below illustrates a high correlation between icecream sales and swimming activity throughout the year. But it is <em>wrong</em> to conclude the sales is the reason for swimming activity or vice versa.</p> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM02W03%5Cfriday_correlation_causation-480.webp 480w,/assets%5Caio2025%5CM02W03%5Cfriday_correlation_causation-800.webp 800w,/assets%5Caio2025%5CM02W03%5Cfriday_correlation_causation-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM02W03%5Cfriday_correlation_causation.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 500px; max-height: 300px; " alt="icecream sales and swimming activity both increase during summer time, but neither of them causes the other. They are both caused by the hot weather with many people enjoying eating icecreams and swimming at the beach/pool" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption"><em>Figure .</em> Icecream sales and swimming activity both increase during summer time, but neither of them causes the other. They are both caused by the hot weather with many people enjoying eating icecreams and swimming at the beach/pool</figcaption> </figure> <p>In practice, it can sometimes be unethical to force researches on causality, so researchers tend to use correlation to conclude if there is a relationship between events. Take smoking and lung cancer or domestic violence and mental illness for example.</p> <p><strong>Regression vs Causation</strong> <br/></p> <ul> <li>Regression does <em>not</em> imply causation.</li> <li>Causation <em>only</em> implies regression if the relationship is linear and quantifiable.</li> </ul> <h3 id="correlation-coefficient"><strong>Correlation Coefficient</strong></h3> <p>Some <strong>common correlation coefficients</strong> are <em>Pearson’s r</em>, <em>Spearman’s rho</em>, <em>Point-biserial</em>, <em>Cramer’s V (Cramer’s $\varphi$)</em>, and <em>Kendall’s tau</em>. We’ll only go over <strong>Pearson’r</strong> in this post.</p> \[\begin{align*} \rho_{xy} = \rho(X, Y) &amp; = \frac{cov(X, Y)}{\sigma_X \cdot \sigma_Y} = \frac{E((X - \mu_X)(Y - \mu_Y))}{\sqrt{var(X)} \cdot \sqrt{var(Y)}} \\ &amp; = \frac{n (\sum_{i} x_k y_k) - (\sum_i x_k) (\sum_i y_k)}{\sqrt{n (\sum_{k} x_k^2) - (\sum_{k} x_k)^2} \cdot \sqrt{n (\sum_{k} y_k^2) - (\sum_{k} y_k)^2}} \end{align*}\] <p>Earlier, we noted that Pearson’s r is insensitive to scaled data. This mathematically means</p> \[\begin{align} \label{corr-coeff-linear-change-invariant} \rho_{xy} = \rho_{uv} \text{ where } u = ax + b, v = cy + d \text{ and } a, b &gt; 0 \end{align}\] <p>as</p> \[\begin{align} \label{corr-coeff-scale-invariant} \rho_{xy} (X, Y) = \rho_{xy} (aX, bY) \text{ where } a, b &gt; 0 \end{align}\] <p>and</p> \[\begin{align} \label{corr-coeff-brightness-shift-invariant} \rho_{xy} (X, Y) = \rho_{xy} (X + c, Y + d). \end{align}\] <p>Computers often represent correlation as a 2-by-2 matrix.</p> \[\begin{equation*} \begin{bmatrix} corr(X, X) &amp; corr(Y, X) \\ corr(X, Y) &amp; corr(Y, Y) \end{bmatrix} = \begin{bmatrix} 1 &amp; corr(Y, X) \\ corr(X, Y) &amp; 1 \end{bmatrix} \end{equation*}\] <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> Use a package in NumPy to compute the covariance between sets of data in the picture. <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM02W03%5Cfriday_exercise_covariance-480.webp 480w,/assets%5Caio2025%5CM02W03%5Cfriday_exercise_covariance-800.webp 800w,/assets%5Caio2025%5CM02W03%5Cfriday_exercise_covariance-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM02W03%5Cfriday_exercise_covariance.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 300px; max-height: 300px; " alt="number of green and red apples in some cities" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption"></figcaption> </figure> <br/> <strong>Exercise 2:</strong> Prove the three equations that show Pearson'r is sensitive to scaled data. <br/> <strong>Exercise 3:</strong> Write a function to compute Pearson's r in four ways (expected value, data points, NumPy package, and Scipy package) and test it with X = [7, 18, 29, 2, 10, 9, 9] and Y = [1, 6, 12, 8, 6, 21, 10]. <br/> <strong>Exercise 4:</strong> For exercise 3, what is noteworthy about the results using pacakges? <br/> <strong>Exercise 5:</strong> What can you say if two random variables have correlation being 1? <br/> <strong>Hints:</strong> Property of correlation coefficient. </div> </details> <hr/> <h2 id="bulb-applications-in-ai">:bulb: <strong>Applications in AI</strong></h2> <h3 id="traffic-sign-matching"><strong>Traffic Sign Matching</strong></h3> <p>In <a href="/blog/2025/math102/">Math for AI 102</a>, we discussed some <strong>image matching</strong> applications. We can use two following concepts to check which images in a given set are most similar.</p> <ul> <li>absolute difference / distance</li> <li>cosine similarity</li> </ul> <p>In addition, we can apply the <strong>correlation concept</strong> to handle the same situation. It is not only insensitive to scaled data in \eqref{corr-coeff-scale-invariant} like cosine similarity but invariant to brightness shift in \eqref{corr-coeff-brightness-shift-invariant} as well. This linear change insusceptibility in \eqref{corr-coeff-linear-change-invariant} facilitates correlation coefficient to perform well on the same images of different brightness.</p> <p><del>DEOMONSTRAION</del></p> <h3 id="image-processing-template-matching"><strong>Image Processing: Template Matching</strong></h3> <p><strong>Template matching</strong> is a technique used to find a portion of an image that matches a template image.</p> <p>We can use all three concepts to resolve this kind of problem. But we know absolute difference and cosine similarity both have more shortcomings compared to correlation coefficient which we’ll use here.</p> <ol> <li>It’s recommended to work with grayscale version of images.</li> <li>Since we already have a template, we can consider it as a <em>sliding window</em> to run all over the input image.</li> <li>At each step, we can compute and store the <em>correlation coefficient</em> between the template and the checking portion in a correlation map.</li> <li>The <em>highest value</em> give our matching.</li> <li>Take the coordinates of the matching portion to highlight it on the original image.</li> </ol> <p>We can also visualize the correlation map by representing it with colors.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># template matching
</span>
<span class="kn">import</span> <span class="n">cv2</span>
<span class="kn">import</span> <span class="n">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">from</span> <span class="n">matplotlib</span> <span class="kn">import</span> <span class="n">pyplot</span> <span class="k">as</span> <span class="n">plt</span>

<span class="c1"># Load image and convert to grayscale
</span><span class="n">image</span> <span class="o">=</span> <span class="n">cv2</span><span class="p">.</span><span class="nf">imread</span><span class="p">(</span><span class="sh">'</span><span class="s">image.png</span><span class="sh">'</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span>
<span class="n">gray</span>  <span class="o">=</span> <span class="n">cv2</span><span class="p">.</span><span class="nf">cvtColor</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="n">cv2</span><span class="p">.</span><span class="n">COLOR_BGR2GRAY</span><span class="p">)</span>

<span class="n">template</span> <span class="o">=</span> <span class="n">cv2</span><span class="p">.</span><span class="nf">imread</span><span class="p">(</span><span class="sh">'</span><span class="s">template.png</span><span class="sh">'</span><span class="p">,</span><span class="mi">0</span><span class="p">)</span>
<span class="n">w</span><span class="p">,</span> <span class="n">h</span> <span class="o">=</span> <span class="n">template</span><span class="p">.</span><span class="n">shape</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>

<span class="c1"># Apply template Matching
</span><span class="n">corr_map</span> <span class="o">=</span> <span class="n">cv2</span><span class="p">.</span><span class="nf">matchTemplate</span><span class="p">(</span><span class="n">gray</span><span class="p">,</span> <span class="n">template</span><span class="p">,</span> <span class="n">cv2</span><span class="p">.</span><span class="n">TM_CCOEFF_NORMED</span><span class="p">)</span>
<span class="n">min_val</span><span class="p">,</span> <span class="n">max_val</span><span class="p">,</span> <span class="n">min_loc</span><span class="p">,</span> <span class="n">max_loc</span> <span class="o">=</span> <span class="n">cv2</span><span class="p">.</span><span class="nf">minMaxLoc</span><span class="p">(</span><span class="n">corr_map</span><span class="p">)</span> 

<span class="c1"># take minimum
</span><span class="n">top_left</span> <span class="o">=</span> <span class="n">max_loc</span>
<span class="n">bottom_right</span> <span class="o">=</span> <span class="p">(</span><span class="n">top_left</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">w</span><span class="p">,</span> <span class="n">top_left</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">h</span><span class="p">)</span>

<span class="c1"># draw a bounding box on the input image
</span><span class="n">cv2</span><span class="p">.</span><span class="nf">rectangle</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="n">top_left</span><span class="p">,</span> <span class="n">bottom_right</span><span class="p">,</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="mi">2</span><span class="p">)</span>

<span class="c1"># save corr map in grayscale
</span><span class="n">corr_map</span> <span class="o">=</span> <span class="p">(</span><span class="n">corr_map</span><span class="o">+</span><span class="mf">1.0</span><span class="p">)</span><span class="o">*</span><span class="mf">127.5</span>
<span class="n">corr_map</span> <span class="o">=</span> <span class="n">corr_map</span><span class="p">.</span><span class="nf">astype</span><span class="p">(</span><span class="sh">'</span><span class="s">uint8</span><span class="sh">'</span><span class="p">)</span> 
<span class="n">cv2</span><span class="p">.</span><span class="nf">imwrite</span><span class="p">(</span><span class="sh">'</span><span class="s">corr_map_grayscale.png</span><span class="sh">'</span><span class="p">,</span> <span class="n">corr_map</span><span class="p">)</span>

<span class="c1"># applyColorMap
</span><span class="n">corr_map</span> <span class="o">=</span> <span class="n">cv2</span><span class="p">.</span><span class="nf">applyColorMap</span><span class="p">(</span><span class="n">corr_map</span><span class="p">,</span> <span class="n">cv2</span><span class="p">.</span><span class="n">COLORMAP_JET</span><span class="p">)</span>

<span class="c1"># save results
</span><span class="n">cv2</span><span class="p">.</span><span class="nf">imwrite</span><span class="p">(</span><span class="sh">'</span><span class="s">corr_map_color.png</span><span class="sh">'</span><span class="p">,</span> <span class="n">corr_map</span><span class="p">)</span>
<span class="n">cv2</span><span class="p">.</span><span class="nf">imwrite</span><span class="p">(</span><span class="sh">'</span><span class="s">result.png</span><span class="sh">'</span><span class="p">,</span> <span class="n">image</span><span class="p">)</span>
</code></pre></div></div> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> Write code with the concepts of absolute diffrence and cosine similarity for template matching. <br/> <strong>Exercise 2:</strong> Explain this computation <code>(corr_map+1.0)*127.5</code>. <br/> <strong>Exercise 3:</strong> Understand the code for template matching and try it out yourself. </div> </details> <hr/> <h2 id="speech_balloon-faq">:speech_balloon: <strong>FAQ</strong></h2> <p>Here are some good questions asked during class. The answers to these questions came from either some admin, TA, STA, or members of AIO2025.</p> <table> <thead> <tr> <th>Questions</th> <th>Answers</th> </tr> </thead> <tbody> <tr> <td>Is correlation coefficient similar to cosine similarity?</td> <td>Correlation coefficient is cosine similarity on verctors subtracted by the sample mean. That is, vectors move to the center. You can say correlation coefficient is a special case of cosine similarity where data is standardized by being mean-centered.</td> </tr> <tr> <td>Can iamges’ correlation coefficient be negative?</td> <td>Yes. It means if a pixel in one image is bright, then that in the other is dark.</td> </tr> </tbody> </table> <p><br/></p> ]]></content><author><name></name></author><category term="AI"/><category term="coding"/><category term="AIO"/><category term="prob-stat"/><summary type="html"><![CDATA[population vs sample, covariance, correlation, regression, causation]]></summary></entry><entry><title type="html">Math for AI 105</title><link href="https://huong-vo.github.io/blog/2025/math105/" rel="alternate" type="text/html" title="Math for AI 105"/><published>2025-07-16T00:00:00+00:00</published><updated>2025-07-16T00:00:00+00:00</updated><id>https://huong-vo.github.io/blog/2025/math105</id><content type="html" xml:base="https://huong-vo.github.io/blog/2025/math105/"><![CDATA[<p><strong>Acknowledgement:</strong> This post is based mainly on Dr. Vinh-Dinh Nguyen’s presentation and slides and partly on on TA, STA, and peers’ responses from the course AIO2025 offered by AI VIETNAM.</p> <p><strong>Relevance:</strong> The audience of this lecture were those who have read <a href="/blog/2025/math104/">Math for AI 104</a> or been familiar with probability and statistics. Note that only basic probability material is introduced here; we’ll pick up specific concepts when in need.</p> <p>In today’s post, we’ll explore some descriptive statistics and probability distribution theory. We’ll also discuss some applications in AI including image processing.</p> <hr/> <p><strong>Descriptive Statistics</strong> refers to tools that summarize and describe features of a dataset, providing us with a clearer picture of the data. There are three main categories: <em>central tendency</em>, <em>variability</em>, and <em>distribution</em>.</p> <h2 id="measure-of-central-tendency"><strong>Measure of Central Tendency</strong></h2> <h3 id="mean-and-expected-value"><strong>Mean and Expected Value</strong></h3> <p><strong>Expected value</strong> is <em>not</em> considered a <em>measure of central tendency</em>, but this concept and <strong>mean</strong> are closely related as they both represent some sort of average.</p> <table class="language-python"> <tr> <th>Mean</th> <th>Expected Value</th> </tr> <tr> <td colspan="2" class="centered_td"> Given a dataset of N items $X = \\{X_1, \cdots, X_N\\}$ </td> </tr> <tr> <td> $$\mu_{X} = \frac{1}{N} \sum_{i=1}^{N} X_i$$ where $X_i$ is a specific outcome. </td> <td> $$E(X) = \sum_{i=1}^{N} X_i P(X = X_i)$$ where $X_i$ is a specific outcome and $P(X = X_i)$ is the probability of a specific outcome. </td> </tr> <tr> <td> <ul> <li> the <strong>average</strong> of all values in the dataset </li> <li> we've <strong>already collected</strong> all raw data </li> <li> <em>Example:</em> Consider playing a guessing game 100 times (in the detailed example below). We can only compute the mean once we've already played 100 times. </li> </ul> </td> <td> <ul> <li> the <strong>weighted average</strong> of all values in the dataset </li> <li> this value is what we expect to occur <strong>before collecting data</strong> </li> <li> <em>Example:</em> Consider playing a guessing game 100 times (in the detailed example below). We have either played it just a few times or never played it before but have some statistics on it. We can use such given data to predict what we may end up. </li> </ul> </td> </tr> </table> <p><br/></p> <details class="bordered-block"> <summary> <strong>Example: Are you willing to play this game?</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Problem statement:</strong> We have information of 100 students in AIO2025: 30 of them love Math while the remaining does not. Let's play a game 100 times where you must guess whether the next student who joins the course loves Math or does not. The rule is <ul> <li> If the student loves Math, you'll lose \$1. </li> <li> If the student doesn't love Math, you'll gain \$1. </li> </ul> <strong>Breakdown:</strong> To make a smart decision, we should know <ul> <li> The money you'll lose/gain if you randomly select a student in AIO2025 and it turns out they love Math. </li> <li> The money you'll lose/gain if you randomly select a student in AIO2025 and it turns out they do not love Math. </li> </ul> <strong>Idea:</strong> With this information, we can analyze the likelihood of outcomes for the two student groups and evaluate whether this game works in your favor. <ol> <li> Compute the money gain/loss after playing 100 times for each case and the whole data. <table class="language-python"> <tr> <th></th> <th>Love Math</th> <th>Not Love Math</th> <th>Total</th> </tr> <tr> <td>Number of students</td> <td class="centered_td">30</td> <td class="centered_td">70</td> <td class="centered_td">100</td> </tr> <tr> <td>Probability</td> <td class="centered_td">$30\% = 0.3$</td> <td class="centered_td">$70\% = 0.7$</td> <td class="centered_td">$100\% = 1.0$</td> </tr> <tr> <td>Outcome</td> <td class="centered_td">$-\$7$</td> <td class="centered_td">$+\$1$</td> <td class="centered_td"></td> </tr> <tr> <td>Expected number gains/losses</td> <td class="centered_td">$100 \cdot (0.3) = 30$</td> <td class="centered_td">$100 \cdot (0.7) = 70$</td> <td class="centered_td"></td> </tr> <tr> <td>Expected gain (a negative number is a loss)</td> <td class="centered_td">$30 \cdot (-1) = -\$30$</td> <td class="centered_td">$70 \cdot (+1) = +\$70$</td> <td class="centered_td">$-30 + 70 = +\$40$</td> </tr> </table> After playing this game 100 times in a roll, you would expect to gain \$40 in total. </li> <li> The average amount of money you expect to gain per game is $40 / 100 = \$0.4$. </li> <li> Using the method above, we compute the expectation on average. $$E[X] = \frac{[(0.3 \cdot 100) \cdot (-1)] + [(0.7 \cdot 100) \cdot (+1)]}{100} = 0.4$$ </li> <li> Canceling out 100, we obtain the <strong>expected value formula</strong>. $$E[X] = \sum x \cdot P(X = x) = [(-1) \cdot (0.3)] + [(+1) \cdot (0.7)] = 0.4$$ </li> So, using the expected value formula, we can predict how much gain/loss it is even before playing the game, which is benificial. </ol> </div> </details> <p><br/></p> <h3 id="median"><strong>Median</strong></h3> <p>Given a dataset of N items $X = \{X_1, \cdots, X_N\}$, median is the value dividing the sorted dataset in half. To obtain the median, we follow two steps.</p> <ol> <li>Sort $X$.</li> <li>Calculate the median $m$: <ul> <li>If $N$ is odd, then $m = X_{\frac{N + 1}{2}}$.</li> <li>If $N$ is even, then $m = \frac{1}{2} (X_{\frac{N}{2}} + X_{\frac{N + 1}{2}})$.</li> </ul> </li> </ol> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> Given $X = \\\{2, 8, 5, 4, 1, 4\\\}$, compute the mean and median of this dataset. <br/> <strong>Exercise 2:</strong> We'll play a similar guessing game as in the example. The rule is that you'll gain \$10 if the student loves Math, otherwise you'll lose \$1. Are you willing to play this time? <br/> <strong>Exercise 3:</strong> If the mean of 25, 29, 25, 32, 24, and x is 27. What is the median? <br/> <strong>Exercise 4:</strong> Given $X = \\\{2, 4. 6, 8, 100\\\}$, does mean or median describe the central tendency better? <br/> <strong>Exercise 5:</strong> If a very large value is added to a dataset, what can you say about mean and median? <br/> <strong>Exercise 6:</strong> Write functions that compute the mean and median of a dataset. </div> </details> <p><br/></p> <h3 id="bulb-applications-in-ai">:bulb: <strong>Applications in AI</strong></h3> <p><strong>Image Processing: Image Blurring using Mean</strong> <br/> The media show pictures or videos and some images are wished to be blurred for privacy or security. We can apply a process called <strong>image blurring</strong> to accomplish this purpose.</p> <p>Using a <strong>average/mean filter (AF)</strong> is a common and simple method to execute this process. The <em>core idea</em> behind it is to smooth out sharp edges and reduce the intensity variations by replacing each pixel’s value with the average of its neighboring pixels including itself. Averaging pixel values is akin to mixing paint colors togther. The more colors we mix, the more the distinctiveness of individual colors is lost.</p> <p><strong>Correlation ~ Convolution</strong> <br/> To implement this idea, we use a technique called <strong>correlation ~ convolution</strong>. We know image data is an ndarray or a matrix where each appropriate set of values is a pixel.</p> <p>Take a <strong>grayscale image</strong> for example. This can be turned into a 2D $m \times n$ matrix. We want to <em>blur the whole image</em>. The code is below.</p> <ol> <li>Determine the shape of neighboring pixel areas, say a $j \times k$ matrix.</li> <li>Take a matrix of that shape and call it a <strong>kernel</strong>.</li> <li>Elements of this matrix are valued $\frac{1}{\text{number of elements}} = \frac{1}{j \cdot k}$ since we want to compute mean.</li> <li>Slide the kernel over each neighboring pixel area from left to right and from top to bottom to obtain those pixels’ mean.</li> <li>Place the mean on the position of the top-left-corner pixel. It’s best practice to store those means in a new array.</li> <li>The new matrix’s shape is $(m - j + 1) \times (n - k + 1)$.</li> </ol> <p>The change in shape makes the new image smaller. To handle this issue, we need a technique calld <strong>padding</strong>—adding layers of numbers surrounding the original matrix. This will take care of the lost region. Here we can</p> <ul> <li>add $j - 1$ and $k - 1$ layers of zeros for the first and second dimension, respectively.</li> <li>copy edge values to the padding region.</li> </ul> <p>Of course, we can also <em>blur part of an image</em>. This part is called <strong>Region of Interest (ROI)</strong>. Recall that an image is an array part of which we can take out by <em>slicing</em>. Check out <a href="/blog/2025/math101/">Math for AI 101</a> for more information about updating a slice of an array.</p> <ul> <li><em>Static image:</em> If we already know the coordinates of ROI, using slicing with specific numbers is suitable.</li> <li><em>Static</em> and <em>moving image:</em> If we don’t know the exact coordinates of ROI, we need to detect that part first and then apply slicing. For a facial area, we can use the <strong>Haar-Cascade Face Detection algorithm</strong>, which returns the top-left-corner coordinates, height, and width of ROI. The face detection set up is as follows.</li> </ul> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">face_cascade</span> <span class="o">=</span> <span class="n">cv2</span><span class="p">.</span><span class="nc">CascadeClassifier</span><span class="p">(</span><span class="sh">'</span><span class="s">haarcascade_frontalface_default.xml</span><span class="sh">'</span><span class="p">)</span>
</code></pre></div></div> <p>For <strong>color images</strong>, we can compute mean for each channel and concatenate three channels in the end to rebuild the third dimension.</p> <p>There are some available packages we can use to apply put this technique into practice.</p> <ul> <li>NumPy: <code class="language-plaintext highlighter-rouge">np.einsum()</code></li> <li>Scipy: <code class="language-plaintext highlighter-rouge">spicy.signal.convolve2d()</code></li> <li>OpenCV: <code class="language-plaintext highlighter-rouge">cv2.filter2D()</code>.</li> </ul> <p><del>DEMONSTRATION MAYBE VIDEO IF TOO HARD TO WRITE</del></p> <figure> <div style="display: flex; gap: 20px; justify-content: center"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM02W03%5Cwednesday_mrbean-480.webp 480w,/assets%5Caio2025%5CM02W03%5Cwednesday_mrbean-800.webp 800w,/assets%5Caio2025%5CM02W03%5Cwednesday_mrbean-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM02W03%5Cwednesday_mrbean.jpg" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" alt="the original image of Mr. Bean" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">The original image of Mr. Bean</figcaption> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM02W03%5Cwednesday_mrbean-480.webp 480w,/assets%5Caio2025%5CM02W03%5Cwednesday_mrbean-800.webp 800w,/assets%5Caio2025%5CM02W03%5Cwednesday_mrbean-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM02W03%5Cwednesday_mrbean.jpg" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" alt="blurring the grayscale image of Mr. Bean" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">The grayscale image is blurred</figcaption> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM02W03%5Cwednesday_mrbean-480.webp 480w,/assets%5Caio2025%5CM02W03%5Cwednesday_mrbean-800.webp 800w,/assets%5Caio2025%5CM02W03%5Cwednesday_mrbean-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM02W03%5Cwednesday_mrbean.jpg" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" alt="Mr. Bean's face is blurred" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">Mr. Bean's face is blurred</figcaption> </figure> </div> <figcaption style="text-align: center; width: 100%; font-size:0.875em; margin-top:-25px;"> <em>Figure 1.</em> Before and after blurring the whole image and a part of it </figcaption> </figure> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># load image and blurring
</span>
<span class="kn">import</span> <span class="n">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="n">cv2</span>

<span class="c1"># load image in grayscale mode
</span><span class="n">image</span> <span class="o">=</span> <span class="n">cv2</span><span class="p">.</span><span class="nf">imread</span><span class="p">(</span><span class="sh">'</span><span class="s">mrbean.jpg</span><span class="sh">'</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>

<span class="c1"># create kernel
</span><span class="n">kernel</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">ones</span><span class="p">((</span><span class="mi">5</span><span class="p">,</span><span class="mi">5</span><span class="p">),</span> <span class="n">np</span><span class="p">.</span><span class="n">float32</span><span class="p">)</span> <span class="o">/</span> <span class="mf">25.0</span>

<span class="c1"># compute mean for each pixel
</span><span class="n">dst</span> <span class="o">=</span> <span class="n">cv2</span><span class="p">.</span><span class="nf">filter2D</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="n">cv2</span><span class="p">.</span><span class="n">CV_8U</span><span class="p">,</span> <span class="n">kernel</span><span class="p">)</span>

<span class="c1"># show images
</span><span class="n">cv2</span><span class="p">.</span><span class="nf">imshow</span><span class="p">(</span><span class="sh">'</span><span class="s">image</span><span class="sh">'</span><span class="p">,</span> <span class="n">image</span><span class="p">)</span>
<span class="n">cv2</span><span class="p">.</span><span class="nf">imshow</span><span class="p">(</span><span class="sh">'</span><span class="s">dst</span><span class="sh">'</span><span class="p">,</span> <span class="n">dst</span><span class="p">)</span>

<span class="c1"># waiting for any keys pressed and close windows
</span><span class="n">cv2</span><span class="p">.</span><span class="nf">waitKey</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">cv2</span><span class="p">.</span><span class="nf">destroyAllWindows</span><span class="p">()</span>
</code></pre></div></div> <p><strong>Image Preprocessing: Image Denoising using Median</strong> <br/> Pictures are often not taken by professionals and in the studio condition. They that can be blurred, underexposed or of unwanted speckles are usually referred to as <em>grainy or noisy images</em>. Hanlding these problems which is a part of <em>image preprocessing</em> is called <strong>image denoising</strong>. Using median is one of the simpliest method to address this.</p> <p>One common type of noise is <strong>salt-and-pepper noise</strong>, that is, an image is speckly. We’ll go over an example of this type.</p> <p>This <strong>median filter (MF)</strong> also uses the <strong>correlation ~ convolution</strong> technique to obtain the median of neighboring pixels. We can use package <code class="language-plaintext highlighter-rouge">cv2.medianBlur()</code> for this purpose.</p> <p>Using median rather than mean in this case is because getting median can exclude too-large or too-small values which are noises. Using mean only makes those values smaller or larger but still keeps noises. The new image is also blurred after median filter but not as much affected as using mean filter.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># denoise salt and pepper
</span>
<span class="kn">import</span> <span class="n">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="n">cv2</span>

<span class="n">img1</span> <span class="o">=</span> <span class="n">cv2</span><span class="p">.</span><span class="nf">imread</span><span class="p">(</span><span class="sh">'</span><span class="s">mrbean_noise.jpg</span><span class="sh">'</span><span class="p">)</span>

<span class="c1"># compute median in each kernel with side=3
</span><span class="n">img2</span> <span class="o">=</span> <span class="n">cv2</span><span class="p">.</span><span class="nf">medianBlur</span><span class="p">(</span><span class="n">img1</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>

<span class="c1"># show images
</span><span class="n">cv2</span><span class="p">.</span><span class="nf">imshow</span><span class="p">(</span><span class="sh">'</span><span class="s">img1</span><span class="sh">'</span><span class="p">,</span> <span class="n">img1</span><span class="p">)</span>
<span class="n">cv2</span><span class="p">.</span><span class="nf">imshow</span><span class="p">(</span><span class="sh">'</span><span class="s">img2</span><span class="sh">'</span><span class="p">,</span> <span class="n">img2</span><span class="p">)</span>

<span class="c1"># waiting for any keys pressed and close windows
</span><span class="n">cv2</span><span class="p">.</span><span class="nf">waitKey</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">cv2</span><span class="p">.</span><span class="nf">destroyAllWindows</span><span class="p">()</span>
</code></pre></div></div> <figure> <div style="display: flex; gap: 20px; justify-content: center"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM02W03%5Cwednesday_mrbean_noise-480.webp 480w,/assets%5Caio2025%5CM02W03%5Cwednesday_mrbean_noise-800.webp 800w,/assets%5Caio2025%5CM02W03%5Cwednesday_mrbean_noise-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM02W03%5Cwednesday_mrbean_noise.jpg" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" alt="Mr. Bean's image with salt-and-pepper noise" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">Mr. Bean's image with salt-and-pepper noise</figcaption> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM02W03%5Cwednesday_mrbean_noise-480.webp 480w,/assets%5Caio2025%5CM02W03%5Cwednesday_mrbean_noise-800.webp 800w,/assets%5Caio2025%5CM02W03%5Cwednesday_mrbean_noise-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM02W03%5Cwednesday_mrbean_noise.jpg" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" alt="Mr. Bean's image with salt-and-pepper removed by median filter" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">Mr. Bean's image with salt-and-pepper removed by median filter</figcaption> </figure> </div> <figcaption style="text-align: center; width: 100%; font-size:0.875em; margin-top:-25px;"> <em>Figure 2.</em> Before and after denoising a salt-and-pepper image </figcaption> </figure> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> Write a non-packaged program that demonnstrates image blurring using mean. <br/> <strong>Hints:</strong> You can use <code>opencv</code> to handle images <br/> <strong>Exercise 2:</strong> Explain why the new matrix has the mentioned shape. <br/> <strong>Exercise 3:</strong> Update the example code above to blur a specified part of a static image. <br/> <strong>Exercise 4:</strong> Check and see what packages do and if they take padding into account. </div> </details> <hr/> <h2 id="measure-of-variability"><strong>Measure of Variability</strong></h2> <h3 id="variance-and-standard-deviation"><strong>Variance and Standard Deviation</strong></h3> <table class="language-python"> <tr> <th>Mean/Expected Value</th> <th>Variance</th> <th>Standard Deviation</th> </tr> <tr> <td></td> <td colspan="2" class="centered_td"> measure <strong>how spread out</strong> data in a given set are from its mean/expected value </td> </tr> <tr> <td>$$\mu_{X} = \frac{1}{N} \sum_{i=1}^{N} X_i$$</td> <td> $$\begin{align*} &amp;\sigma^2 = var(X) \\ &amp;= \frac{1}{N} \sum_{i=1}^{N} (X_i - \mu)^2 \\ &amp;= E ((X - \mu)^2) \end{align*}$$ </td> <td>$$\sigma = \sqrt{var(X)}$$</td> </tr> <tr> <td>$$E(X) = \sum_{i=1}^{N} X_i P(X_i)$$</td> <td> $$\begin{align*} &amp;\sigma^2 = var(X) \\ &amp;= \sum_{i=1}^{N} (X_i - E(X))^2 \cdot P(X_i) \\ &amp;= E \left((X - E(X)) ^2 \right) \\ &amp;= E(X^2) - (E(X))^2 \end{align*}$$ </td> <td>$$\sigma = \sqrt{var(X)}$$</td> </tr> <tr> <th scope="row">Definition and Properties</th> <td> <ul> <li> the average of squared differences from the mean/expected value </li> <li> squared distance between data points and the mean/expected value </li> <li> measured in squared units </li> </ul> </td> <td> <ul> <li> the square root of the variance </li> <li> typical distance between data points and the mean/expected value </li> <li> measured in a unit similar to the mean/expected value's unit </li> </ul> </td> </tr> </table> <p><br/></p> <h3 id="bulb-application-in-ai">:bulb: <strong>Application in AI</strong></h3> <p><strong>Image Processing: Image Texture using Standard Deviation</strong> <br/></p> <p>When identifying an object, computers may just need to know its shape. That is, we focus on the boundary of objects in an image rather than their color. This is known as <strong>image texture</strong>—local patterns created by differences in relative color arrangement in an image. We will discuss this concept in detail later in this <strong>AI series</strong>.</p> <p>Since we’re only interested in the edge of objects in an image, it suffices to just work with a grayscale version of an image for the sake of simplicity. This <strong>standard deviation filter</strong> also uses the <strong>correlation ~ convolution</strong> technique to obtain the standard deviation of neighboring pixels.</p> <ul> <li>The standard deviation remains small when the kernel moves over regions with little color variation or uniform intensity.</li> <li>The standard deviation increases sharply when the kernel moves across a boundary from a consistent-color region to a region with contrasting colors.</li> </ul> <figure> <div style="display: flex; gap: 20px; justify-content: center"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM02W03%5Cwednesday_church_gray-480.webp 480w,/assets%5Caio2025%5CM02W03%5Cwednesday_church_gray-800.webp 800w,/assets%5Caio2025%5CM02W03%5Cwednesday_church_gray-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM02W03%5Cwednesday_church_gray.jpg" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" alt="an grayscale image" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">An grayscale image of a church</figcaption> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM02W03%5Cwednesday_edge_s2-480.webp 480w,/assets%5Caio2025%5CM02W03%5Cwednesday_edge_s2-800.webp 800w,/assets%5Caio2025%5CM02W03%5Cwednesday_edge_s2-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM02W03%5Cwednesday_edge_s2.jpg" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" alt="first image texture of the church image" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">First image texture of the church image</figcaption> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM02W03%5Cwednesday_edge_s3-480.webp 480w,/assets%5Caio2025%5CM02W03%5Cwednesday_edge_s3-800.webp 800w,/assets%5Caio2025%5CM02W03%5Cwednesday_edge_s3-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM02W03%5Cwednesday_edge_s3.jpg" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" alt="second image texture of the church image" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">Second image texture of the church image</figcaption> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM02W03%5Cwednesday_edge_s4-480.webp 480w,/assets%5Caio2025%5CM02W03%5Cwednesday_edge_s4-800.webp 800w,/assets%5Caio2025%5CM02W03%5Cwednesday_edge_s4-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM02W03%5Cwednesday_edge_s4.jpg" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" alt="third image texture of the church image" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">Third image texture of the church image</figcaption> </figure> </div> <figcaption style="text-align: center; width: 100%; font-size:0.875em; margin-top:-25px;"> <em>Figure 3.</em> Before and after extracting image texture </figcaption> </figure> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> Prove the third equality of variance formula for expected value. <br/> <strong>Exercise 2:</strong> Write a function to compute variance for mean with and without built-in methods. <br/> <strong>Exercise 3:</strong> Given $A = \\\{2, 4, 6, 8, 10\\\}$ and $A = \\\{5, 5, 6, 6, 7\\\}$, what can you say about their standard deviations without calculating them? <br/> <strong>Exercise 4:</strong> Assume that there are two classes including A and B. Each of A and B has an average score of 6.95. A's variance is 0.05 while B's is 4.62. What can you say about their scores? <s>IMAGE</s> <br/> <strong>Exercise 5:</strong> Does AF or MF keep edges better? </div> </details> <p><br/></p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># extract image texture
</span>
<span class="kn">import</span> <span class="n">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="n">cv2</span>
<span class="kn">import</span> <span class="n">math</span>
<span class="kn">from</span> <span class="n">scipy.ndimage.filters</span> <span class="kn">import</span> <span class="n">generic_filter</span>

<span class="c1"># convert a color image to grayscale
</span><span class="n">img</span>  <span class="o">=</span> <span class="n">cv2</span><span class="p">.</span><span class="nf">imread</span><span class="p">(</span><span class="sh">'</span><span class="s">img.jpg</span><span class="sh">'</span><span class="p">)</span>
<span class="n">gray</span> <span class="o">=</span> <span class="n">cv2</span><span class="p">.</span><span class="nf">cvtColor</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="n">cv2</span><span class="p">.</span><span class="n">COLOR_BGR2GRAY</span><span class="p">)</span>
<span class="n">cv2</span><span class="p">.</span><span class="nf">imwrite</span><span class="p">(</span><span class="sh">'</span><span class="s">edge_s1.jpg</span><span class="sh">'</span><span class="p">,</span> <span class="n">gray</span><span class="p">)</span>

<span class="c1"># compute standard deviation in each kernel of size=7
</span><span class="n">x</span> <span class="o">=</span> <span class="n">gray</span><span class="p">.</span><span class="nf">astype</span><span class="p">(</span><span class="sh">'</span><span class="s">float</span><span class="sh">'</span><span class="p">)</span>
<span class="n">x_filt</span> <span class="o">=</span> <span class="nf">generic_filter</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">np</span><span class="p">.</span><span class="n">std</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="mi">7</span><span class="p">)</span>
<span class="n">cv2</span><span class="p">.</span><span class="nf">imwrite</span><span class="p">(</span><span class="sh">'</span><span class="s">edge_s2.jpg</span><span class="sh">'</span><span class="p">,</span> <span class="n">x_filt</span><span class="p">)</span>

<span class="c1"># reassign small values (no sudden change in standard deviation)
# blacken insignificant areas or edges
</span><span class="n">x_filt</span><span class="p">[</span><span class="n">x_filt</span> <span class="o">&lt;</span> <span class="mi">20</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">cv2</span><span class="p">.</span><span class="nf">imwrite</span><span class="p">(</span><span class="sh">'</span><span class="s">edge_s3.jpg</span><span class="sh">'</span><span class="p">,</span> <span class="n">x_filt</span><span class="p">)</span>

<span class="c1"># lighten significant edges
</span><span class="n">x_filt</span> <span class="o">=</span> <span class="n">x_filt</span><span class="o">*</span><span class="mf">2.5</span>
<span class="n">cv2</span><span class="p">.</span><span class="nf">imwrite</span><span class="p">(</span><span class="sh">'</span><span class="s">edge_s4.jpg</span><span class="sh">'</span><span class="p">,</span> <span class="n">x_filt</span><span class="p">)</span>
</code></pre></div></div> <hr/> <h2 id="probability-distribution-function"><strong>Probability Distribution Function</strong></h2> <p>A <strong>probability distribution</strong> is a function that describes the likelihood of occurence of possible events, that is, random variables. It is used to model the behavior of <em>random phenomena</em> from a dataset. There are three main types of probability distribution and we’ll discuss them in a moment.</p> <ul> <li><strong>Probability Mass Function (PMF)</strong></li> <li><strong>Probability Density Function (PDF)</strong></li> <li><strong>Cumulative Distribution Function (CDF)</strong></li> </ul> <h3 id="discrete-pmf-and-cdf"><strong>Discrete: PMF and CDF</strong></h3> <p><strong>PMF</strong> measures the probability of a given single value x occuring in a countable dataset, denoted by $p_X(x) = P(X = x)$. <strong>CDF</strong> measures the probability of values occuring from the beginning up to x in a countable dataset, denoted by $F_X(x) = P(X \le x)$.</p> <p>Take rolling a dice for example. We have $p(3) = \frac{1}{6}$ and $F(3) = p(1) + p(2) + p(3) = \frac{3}{6}$.</p> <figure> <div style="display: flex; flex-wrap:wrap; column-gap: 20px; justify-content: center"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM02W03%5Ctuesday_graph_pmf_2-480.webp 480w,/assets%5Caio2025%5CM02W03%5Ctuesday_graph_pmf_2-800.webp 800w,/assets%5Caio2025%5CM02W03%5Ctuesday_graph_pmf_2-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM02W03%5Ctuesday_graph_pmf_2.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 320px; max-height: 400px; " alt="pmf for rolling a fair dice, each value is 1/6" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">PMF for rolling a fair dice</figcaption> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM02W03%5Ctuesday_graph_cdf-480.webp 480w,/assets%5Caio2025%5CM02W03%5Ctuesday_graph_cdf-800.webp 800w,/assets%5Caio2025%5CM02W03%5Ctuesday_graph_cdf-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM02W03%5Ctuesday_graph_cdf.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 320px; max-height: 400px; " alt="pmf for rolling a fair dice" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">CDF for rolling a fair dice</figcaption> </figure> </div> <figcaption style="text-align: center; width: 100%; font-size:0.875em; margin-top:-25px;"> <em>Figure 4.</em> PDF and CDF for discrete outcomes </figcaption> </figure> <h3 id="continuous-pdf-and-cdf"><strong>Continuous: PDF and CDF</strong></h3> <p>For experiments like assessing grades for an examination, there are so many outcomes that the probability to take on any particular value is <em>always</em> 0. We can hardly use PMF to understand the likelihood of a single value occuring in a dataset. This is where PDF comes into play.</p> <p><strong>PDF</strong> measures the relative likelihood of a random variable to take a value near x, denoted by $f(x)$. It is used to specify the <strong>probability</strong> that a given single value x falls within an interval, computed by integrate PDF $\int_{a}^{b} f(x) \,dx$. <strong>CDF</strong> measures the probability of values occuring from the beginning up to x in an uncountable dataset, denoted by $F_X(x) = P(X \le x)$.</p> <p><strong>Gaussian distribution function</strong> \(f(x) = \frac{1}{\sigma \sqrt{2\pi}} \exp^{-\frac{(x-\mu)^2}{2\sigma^2}}\) is a commonly used PDF. The total area under this curve is <em>always</em> 1 and</p> <ul> <li>the area within 1 𝜎, that is, $[\mu - \sigma, \mu + \sigma]$ is about 62.27%.</li> <li>the area within 2 𝜎, that is, $[\mu - 2\sigma, \mu + 2\sigma]$ is about 95.45%.</li> <li>the area within 3 𝜎, that is, $[\mu - 3\sigma, \mu + 3\sigma]$ is about 99.73%.</li> </ul> <figure style="margin-top: 0; padding-top: 0;"> <iframe src="https://www.geogebra.org/classic/wruhwczy?embed" title="Gaussian (Normal) Probability Density Function" frameborder="0" height="700px" width="100%" style="border: 1px solid #ddd; margin-top: 0; padding-top: 0;" allowfullscreen="">Your browser does not support iframes. View the interactive applet <a href="https://www.geogebra.org/classic/wruhwczy" target="_blank" rel="noopener">here</a>.</iframe> <figcaption style="font-size:0.875em"> <em>Figure 5.</em> The figure illustrates the Gaussian (normal) distribution. The curve shows how the likelihood is distributed around the mean 𝜇, with the standard deviation 𝜎 indicating the spread. You can adjust sliders to change the bounds and see how the shaded probability region updates in real time. Click on the symbol in the bottom right corner to open the applet in full-screen mode. If the applet does not load, you can open it directly <a href="https://www.geogebra.org/classic/wruhwczy" target="_blank" rel="noopener">here</a>.</figcaption> </figure> <p><del>EXAMPLE DEMONSTRATION</del></p> <hr/> <h3 id="bulb-application-in-ai-1">:bulb: <strong>Application in AI</strong></h3> <p><strong>Measure of Frequency Distribution: Histogram</strong> <br/> Given a <strong>grayscale</strong> image, we can convert it into an array or matrix. Using such information, we can extract and arrange values in a <strong>frequency histogram</strong>. Such a histogram represents the frequency distribution of pixel values from the image. Pixel values range from 0 to 255 and we call each value a <strong>bin</strong>, so we have 256 bins in total. This allows us to imply if a grayscale image is bright or dark <em>without</em> even seeing the image as values a pixel gets lighter and lighter as it goes up.</p> <p>The output of a frequency histogram depends on the number of pixels in an image as well as each bin. Thus, the output varies among images. We can normalize the output so that it acts as the <em>probability density</em>. This results in a <strong>density histogram</strong>.</p> <p>Let $N$ be the total number of pixels of an image and $n_b$ be the number of pixels in bin $b$. The density value of bin b is</p> \[h[b] = \frac{n_b}{N}.\] <p>So the <em>(probability) cumulative</em> value up to bin b is computed as follows. We can use these values to construct a <strong>cumulative histogram</strong>—displaying how pixel values accumulate across the intensity range.</p> \[H[b] = \sum_{k=0}^{b} h[k].\] <p>In fact, using any of these histograms allows us to determine the brightness of an image.</p> <p><del>3 DEMONSTRATION from slide 54 describe how and why they are darker or brighter in each image caption</del></p> <p><strong>Histogram Equalization</strong> <br/> Besides determining how bright or dark an image is, we can even change the brightness of an image by utilizing the probability distribution functions.</p> <p>The core idea behind <strong>contrast adjustment</strong> is to modifying the distribution of the density histogram of an image so that the cumulative histogram approximates to a <em>linear relation</em>. That is, we wish <strong>uniformly redistribute</strong> the values of the frequency or density histogram. This helps the resulting image has a uniform intensity distribution. This process is known as <strong>histogram equalization</strong>.</p> <p>Essentially, the ideal scenario is when all the bins have the same number of values. This gives a straight line when accumulating those values. It’s like a dice roll experiment.</p> <p><del>DEMONSTRATION</del></p> <p>We do <em>not</em> use density histogram here because it only describes individual bins—there is no relation between intensity values. Instead, to achieve this purpose, we use the <strong>cumulative histogram</strong>, which tells us how pixel intensities build up across the image. The formula that we’ll explore soon maps the original pixel intensity $b$ to a new value proportional to $H[b]$.</p> <ul> <li>A very dark pixel like 5 may be pushed to a <em>brighter intensity</em>.</li> <li>The <em>accumulation</em> levels out the brightness by <em>spreading values uniformly</em>. <ul> <li>If over two thirds of pixels are valued in [0, 50], they’ll be mapped to high intensity values like [0, 200].</li> <li>And only a quarter of the rest falls in [50, 100], they’ll be mapped to just slightly higher intensity values or a narrow range like [200, 225].</li> </ul> <p>This way, no bin would get way too much addition than others.</p> </li> </ul> <p>Let $L$ be the number of gray/intensity levels. It corresponds to the number of possible values a pixel can take within an image. Each pixel in this case ranges from 0 to 255, so there can be 256 different gray levels.</p> \[b_{new} = \lfloor (L - 1) \cdot H[b] + 0.5 \rfloor.\] <p>Note that the cumulative values are in [0, 1], but the gray values are in [0, 255]. This is the reason to employ the multiplication and floor function.</p> <p>We can also apply a similar method for a <strong>color image</strong>.</p> <details class="bordered-block"> <summary> <strong>Example with a 1D Array for Simplicity</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Problem statement:</strong> Given the following matrix. Demonstrate histograms before and after the histogram equalization process. $$\begin{bmatrix} 1 &amp; 2 &amp; 7 &amp; 5 &amp; 6 \\ 7 &amp; 2 &amp; 3 &amp; 4 &amp; 5 \\ 0 &amp; 1 &amp; 5 &amp; 7 &amp; 3 \\ 1 &amp; 2 &amp; 5 &amp; 6 &amp; 7 \\ 6 &amp; 1 &amp; 0 &amp; 3 &amp; 4 \end{bmatrix}$$ <strong>Breakdown:</strong> <ul> <li> A table contains all the calculations above. </li> <ul> <li> N = 25 since it is a 5-by-5 matrix. </li> <li> L = 8 since the values run from 0 to 7. </li> </ul> <li> Show original and equalized histograms to see how new values change the graphs. </li> </ul> <strong>Idea:</strong> We apply the histogram equalization process. <table class="language-python"> <tr> <th>$b$</th> <th>Frequency</th> <th>PMF ~ $h[b]$</th> <th>CDF ~ $H[b]$</th> <th>$(L - 1) \cdot H[b]$</th> <th>$b_{new}$</th> </tr> <tr> <td class=".centered_td">0</td> <td class=".centered_td">2</td> <td class=".centered_td">0.08</td> <td class=".centered_td">0.08</td> <td class=".centered_td">0.56</td> <td class=".centered_td">1</td> </tr> <tr> <td class=".centered_td">1</td> <td class=".centered_td">4</td> <td class=".centered_td">0.16</td> <td class=".centered_td">0.24</td> <td class=".centered_td">1.68</td> <td class=".centered_td">2</td> </tr> <tr> <td class=".centered_td">2</td> <td class=".centered_td">3</td> <td class=".centered_td">0.12</td> <td class=".centered_td">0.36</td> <td class=".centered_td">2.52</td> <td class=".centered_td">3</td> </tr> <tr> <td class=".centered_td">3</td> <td class=".centered_td">3</td> <td class=".centered_td">0.12</td> <td class=".centered_td">0.48</td> <td class=".centered_td">3.36</td> <td class=".centered_td">3</td> </tr> <tr> <td class=".centered_td">4</td> <td class=".centered_td">2</td> <td class=".centered_td">0.08</td> <td class=".centered_td">0.56</td> <td class=".centered_td">3.92</td> <td class=".centered_td">4</td> </tr> <tr> <td class=".centered_td">5</td> <td class=".centered_td">4</td> <td class=".centered_td">0.16</td> <td class=".centered_td">0.72</td> <td class=".centered_td">5.04</td> <td class=".centered_td">5</td> </tr> <tr> <td class=".centered_td">6</td> <td class=".centered_td">3</td> <td class=".centered_td">0.12</td> <td class=".centered_td">0.84</td> <td class=".centered_td">5.88</td> <td class=".centered_td">6</td> </tr> <tr> <td class=".centered_td">7</td> <td class=".centered_td">4</td> <td class=".centered_td">0.16</td> <td class=".centered_td">1</td> <td class=".centered_td">7.0</td> <td class=".centered_td">7</td> </tr> </table> <br/> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM02W03%5Cwednesday_histogram_example-480.webp 480w,/assets%5Caio2025%5CM02W03%5Cwednesday_histogram_example-800.webp 800w,/assets%5Caio2025%5CM02W03%5Cwednesday_histogram_example-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM02W03%5Cwednesday_histogram_example.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 400px; max-height: 500px; " alt="frequency and cumulative histograms before and after equalization" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">Frequency and cumulative histograms before and after equalization</figcaption> </figure> </div> </details> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> Prove the areas within standard deviations are as stated above. </div> </details> <hr/> <h2 id="speech_balloon-faq">:speech_balloon: <strong>FAQ</strong></h2> <p>Here are some good questions asked during class. The answers to these questions came from either some admin, TA, STA, or members of AIO2025.</p> <table> <thead> <tr> <th>Questions</th> <th>Answers</th> </tr> </thead> <tbody> <tr> <td>How can objects in a video be blurred?</td> <td>A video is a collection of frames, so we separate a video into images and apply to each of them.</td> </tr> <tr> <td>Can the Haar-Cascade Face Detection Algorithm be applied to animals?</td> <td>Yes. It can also detect multiple faces.</td> </tr> <tr> <td>Can an image be denoised while keeping its shape and image quality?</td> <td>Yes. Transformer is a way to go.</td> </tr> </tbody> </table> <p><br/></p> ]]></content><author><name></name></author><category term="AI"/><category term="coding"/><category term="AIO"/><category term="prob-stat"/><summary type="html"><![CDATA[mean, median, variance, PDF, PMF, CDF, historgram]]></summary></entry><entry><title type="html">Math for AI 105 Warm-up</title><link href="https://huong-vo.github.io/blog/2025/math-pre105/" rel="alternate" type="text/html" title="Math for AI 105 Warm-up"/><published>2025-07-15T00:00:00+00:00</published><updated>2025-07-15T00:00:00+00:00</updated><id>https://huong-vo.github.io/blog/2025/math-pre105</id><content type="html" xml:base="https://huong-vo.github.io/blog/2025/math-pre105/"><![CDATA[<p><strong>Acknowledgement:</strong> This post is based mainly on TA. Vo Huynh Quang Nguyen’s presentation and slides and partly on on TA, STA, and peers’ responses from the course AIO2025 offered by AI VIETNAM.</p> <p><strong>Relevance:</strong> The audience of this lecture were those who have read <a href="/blog/2025/math104/">Math for AI 104</a> or been familiar with probability and statistics. Note that only basic probability material is introduced here; we’ll pick up specific concepts when in need.</p> <p>In today’s post, we’ll explore basic probability concepts: random variables, probability distribution, and descriptive statistics.</p> <hr/> <h2 id="random-variable-rv"><strong>Random Variable (RV)</strong></h2> <h3 id="definition"><strong>Definition</strong></h3> <p>Let’s use some beginner-friendly scenarios to introduce the idea of a <em>random variable</em>.</p> <table> <thead> <tr> <th>Random Experiment</th> <th>Random Variable</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>Picking a candy from a jar</td> <td>The candy color</td> <td>If you get red, X = Red. <br/> If you get green, X = Green.</td> </tr> <tr> <td>Spinning a wheel at a fair</td> <td>The number of tickets you win</td> <td>If it lands on “10 tickets”, X = 10. <br/> If it lands on “good luck next time”, X = 0.</td> </tr> <tr> <td>An entire soccer match</td> <td>The number of goals</td> <td>If the final score is 2-1, X = 3.</td> </tr> <tr> <td>Throwing a dart</td> <td>The distance from the landing to the bullseye</td> <td>If you hit close, X might be 0.5 inch. <br/> If you miss badly, X might be 5 inches.</td> </tr> <tr> <td>Throwing a dart three times</td> <td>The number of times the dart lands within 2 inches from the bullseye</td> <td>If you land within the range once, X = 1. <br/> If you lands outside of the range once, X = 4.</td> </tr> </tbody> </table> <p><br/></p> <p>From above cases, we can think of a <em>random variable</em> as a real number depending on the outcome of a random experiment.</p> <p>Formally, a <strong>random variable $X$</strong> is a function $X: \Omega \to \mathbb{R}$, $s \mapsto X(s)$ where $\Omega$ is the sample space—set of outcomes and $\mathbb{R}$ is the set of real numbers. Let’s delve into two types of random variables and their examples to nail this concept.</p> <h3 id="discrete-rv---probability"><strong>Discrete RV - Probability</strong></h3> <p>A <strong>discrete random variable $X$</strong> is a function $X: \Omega \to \mathbb{R}$ where $\Omega$ is a finite or countably infinite set. Since the range of this function contains numbers representing mutually exclusive events, we can measure <em>probability on random variables</em> and the sum of all those must be 1.</p> <p>Probability on random variables are known as <strong>probability distribution</strong>. Probability mass and density are common probability distributions.</p> <p><strong>Probability Mass Function (PMF)</strong>, denoted by $p(x)$ or $p_X(x)$, measures the probabiltiy of possible values of a discrete random variable, that is, $p(x) = p_X(x) = P(X = x)$. <br/></p> <p><strong>Cummulative Distribution Function (CDF)</strong>, denoted by $F(x)$ or $F_X(x)$, is the cumulative probability of of all possible values less than equal to a specific number, that is, $F_X(x) = P(X \le x)$. <br/></p> <p>We’ll see examples demonstrated in two following tables.</p> <table class="language-python"> <tr> <th>Concept</th> <th>Description/Meaning</th> <th>Result</th> </tr> <tr> <th scope="row">Experiment</th> <td>Toss a fair coin 3 times in a roll</td> <td>Each time has 2 possible outcomes including head and tail</td> </tr> <tr> <th scope="row">Sample Space</th> <td>H is a head and T is a tail</td> <td> $\Omega = \\\{HHH, HHT, HTH, HTT, THH, THT, TTH, TTT\\\}$ <br/> $n(\Omega) = 2^3 = 8$ </td> </tr> <tr> <th scope="row" rowspan="2">Random Variable</th> <td>The number of heads in the sequence</td> <td> $X(s) \in \\\{0, 1, 2, 3\\\}$ <ul> <li> $X(s) = 2 \Rightarrow s \in \\\{\text{HHT, HTH, THH}\\\} \Rightarrow n(X(s)) = 3$ </li> <li> $X(s) = 0 \Rightarrow s \in \\\{\text{TTT}\\\} \Rightarrow n(X(s)) = 0$ </li> <li> $1 &lt; X(s) \le 3 \Rightarrow s \in \\\{\text{HHH, HHT, HTH, THH}\\\} \Rightarrow n(X(s)) = 4$ </li> </ul> </td> </tr> <tr> <td>The index of the first head (0 if there is no head)</td> <td> $X(s) \in \\\{0, 1, 2, 3\\\}$ <ul> <li> $s = TTT \Rightarrow X(s) = 0$ </li> <li> $s = TTH \Rightarrow X(s) = 3$ </li> <li> $s = HTH \Rightarrow X(s) = 1$ </li> </ul> </td> </tr> </table> <p><br/></p> <p>Below, consider the number of heads in the sequence as a random variable.</p> <table class="language-python"> <tr> <th>Random Variables</th> <th>PMF</th> <th>CDF</th> </tr> <tr> <td class="centered_td">$X(s) = 2 \Rightarrow P(X = 2) = \frac{3}{8}$</td> <td class="centered_td">$p(2) = \frac{3}{8}$</td> <td class="centered_td">:x:</td> </tr> <tr> <td class="centered_td">$1 &lt; X(s) \le 3 \Rightarrow P(1 &lt; X \le 3) = \frac{4}{8}$</td> <td class="centered_td">:x:</td> <td class="centered_td">:x:</td> </tr> <tr> <td class="centered_td">$X(s) \le 2 \Rightarrow X \in \\\{0, 1, 2\\\} \Rightarrow P(X \le 2) = \frac{7}{8}$</td> <td class="centered_td">:x:</td> <td class="centered_td">$F(2) = \frac{7}{8}$</td> </tr> </table> <p><br/></p> <figure> <div style="display: flex; flex-wrap:wrap; column-gap: 20px; justify-content: center"> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM02W03%5Ctuesday_graph_pmf_1-480.webp 480w,/assets%5Caio2025%5CM02W03%5Ctuesday_graph_pmf_1-800.webp 800w,/assets%5Caio2025%5CM02W03%5Ctuesday_graph_pmf_1-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM02W03%5Ctuesday_graph_pmf_1.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 300px; max-height: 500px; " alt="pmf for pairs of value and probability: 1-0.2, 2-0.5, 3-0.3" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">PMF for discrete RV</figcaption> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM02W03%5Ctuesday_graph_pmf_2-480.webp 480w,/assets%5Caio2025%5CM02W03%5Ctuesday_graph_pmf_2-800.webp 800w,/assets%5Caio2025%5CM02W03%5Ctuesday_graph_pmf_2-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM02W03%5Ctuesday_graph_pmf_2.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 340px; max-height: 530px; " alt="pmf for rolling a fair dice, each value is 1/6" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">PMF for rolling a fair dice</figcaption> </figure> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets%5Caio2025%5CM02W03%5Ctuesday_graph_cdf-480.webp 480w,/assets%5Caio2025%5CM02W03%5Ctuesday_graph_cdf-800.webp 800w,/assets%5Caio2025%5CM02W03%5Ctuesday_graph_cdf-1400.webp 1400w," type="image/webp" sizes="95vw"/> <img src="/assets%5Caio2025%5CM02W03%5Ctuesday_graph_cdf.png" class="img-fluid rounded z-depth-1 mx-auto d-block" width="100%" height="auto" style=" max-width: 400px; max-height: 500px; " alt="pmf for rolling a fair dice" data-zoomable="" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> <figcaption class="caption">CDF for rolling a fair dice</figcaption> </figure> </div> <figcaption style="text-align: center; width: 100%; font-size:0.875em; margin-top:-25px;"> <em>Figure 1.</em> PDF and CDF for discrete random variables </figcaption> </figure> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> Use the example in the second example, compute the probability, PMF, and PDF of the following if applicable. <ol> <li> $X = 1$ </li> <li> $X &lt; 3$ </li> <li> $X \le -1$ </li> <li> $X \le 3$ </li> <li> $X \le 4$ </li> </ol> <strong>Exercise 2:</strong> What is the sum of all PMFs $\sum P(X = x)$ in exercise 1 and for an abitrary random experiment? <br/> <strong>Exercise 3:</strong> Throw a die twice. X is the sum of numbers rolled. What is p(7)? What is F(3)? <br/> <strong>Exercise 4:</strong> Extract 3 balls from a box containing an equal number of white and black balls. Put the ball back into the box after each extraction. How many white balls can occur and what are the associated probabilities, PMFs, and PDFs? </div> </details> <p><br/></p> <h3 id="continuous-rv---probability"><strong>Continuous RV - Probability</strong></h3> <p>A <strong>continuous random variable $X$</strong> is a function $X: \Omega \to \mathbb{R}$ where $\Omega$ is an uncountabe set. The <em>normal distribution</em> mentioned in <a href="/blog/2025/math104/">Math for AI 104</a> is one of the most well-known probability models for continuous random variables .</p> <p>Let’s see a couple of examples.</p> <ol> <li>Imagine we’re looking at the second hand of a clock and take a random snapshot at an unknown moment during a one-minute period. <ul> <li>Define $X(0, 12]$ = angle $\theta$ (in degrees) between the second hand and the 12 o’clock position.</li> <li>The second hand sweeps smoothly in a full circle ($360^\circ$) every minute.</li> <li>So $X \in (0, 360]$.</li> </ul> </li> <li>Adult female/male heights. <ul> <li>A person’s height can be any positive real number.</li> <li>So $X \in (0, \infty)$.</li> </ul> </li> </ol> <p><strong>PMF for continuous random variables</strong> is <em>always</em> 0 since the sample space is so large that it makes the occurence of one value become insignificant. On the other hand, <strong>PDF for continuous random variables</strong> can be computed using the <em>Gaussian function</em>. <br/></p> <hr/> <h2 id="mathematical-expectations"><strong>Mathematical Expectations</strong></h2> <p>Below are some concepts used for <strong>discrete random variables</strong>. We’ll briefly go over mathematical expectations for <em>continous random variables</em> right after.</p> <h3 id="expected-value"><strong>Expected Value</strong></h3> <p><strong>Expected value</strong> of a discrete random variable X, denoted by $E[X]$, represents the weighted average value of X and computed as follows. It is often referred to as the mean of probability distribution but tremendously more useful when the size of the sample space is unknown.</p> \[E[X] = \sum_{k} x_k \cdot P(X = x_k) = \sum_{k} x_k \cdot p(x_k)\] <p>Some <em>important properties</em> of expected values are</p> <ul> <li>$E[\alpha X] = \alpha E[X]$</li> <li>$E[\alpha X + b] = \alpha E[X] + b$.</li> </ul> <h3 id="variance"><strong>Variance</strong></h3> <p><strong>Variance</strong> of a random variable X, denoted as $var(X)$, is the weighted average of squared distance from the mean. The reason behind squared distance is that we want to variance to be non-negative for further statistics concepts. On top of that, squared is preferred over absolute value since such a squared function is differentiable everywhere. Use <code class="language-plaintext highlighter-rouge">np.var()</code>.</p> \[var(X) = E[(X - \mu)^2] = \sum_{k} (x_k - \mu)^2 \cdot p(x_k) = E[X^2] - \mu^2 = E[X^2] - E[X]^2\] <p>where $\mu = E[X]$.</p> <h3 id="standard-deviation"><strong>Standard Deviation</strong></h3> <p><strong>Standard deviation</strong> of a random variable X, denoted $\sigma(X)$, is the weighted average of distance from the mean. Use <code class="language-plaintext highlighter-rouge">np.std()</code>.</p> \[\sigma(X) = \sqrt{var(X)}\] <p>:pushpin: <strong>Note:</strong> (discussed more in <a href="/blog/2025/math106/">Math for AI 106</a>)</p> <ol> <li> <p>If outcomes are equally likely to happen and $N$ is the entire population’s size, we have</p> \[E[X] = \frac{1}{N} \sum_{k} x_k \text{ and } var(X) = \frac{1}{N} \sum_{k} (x_k - \mu)^2.\] </li> <li> <p>If we only take a sample of size $n$, then N becomes $n - 1$. Add another argument <code class="language-plaintext highlighter-rouge">ddof=1</code> when using NumPy.</p> </li> </ol> <h3 id="continuous-random-variable"><strong>Continuous Random Variable</strong></h3> <p>Since PMFs for continuous random variables are always 0, we can only apply the idea to compute expected value, variance, and standard deviations rather than exactly the same formulas. Here, we use integral for <strong>expected value</strong> which is</p> \[\mu = E[X] = \int_{-\infty}^{\infty} x f(x) \,dx.\] <p>This integral can be done using either <em>Riemann’s sum</em> or <em>Lebesgue integral</em> (and maybe other integrals). The former is applied for “nice” distribution while the latter can handle more complicated distributions. This is partly because the Lebesgue integral takes weighted coefficients into account better. <em>Computers use the Lebesgue integral as its default method.</em></p> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> Toss a coin three times in a roll. Let a random variable be the number of heads in each sequence. Compute the expected value. <br/> <strong>Exercise 2:</strong> Toss a coin twice. Let a random variable be the number of heads in each sequence. Compute the variance. <br/> <strong>Exercise 3:</strong> Compute the expected value, variance, and standard derivation given X as follows. <ol> <li> $X = \\{1, 3, 4, 4\\}$ </li> <li> $X = \\{1, 2, 8, 5\\}$ </li> <li> $X = \\{1, 3, 4, 4\\}$ and corresponding $p(X) = \\{0.1, 0.2, 0.4, 40.3\\}$ </li> <li> $X = \\{0, 1\\}$ and corresponding $p(X) = \\{p, 1 - p\\}$ </li> <li> $X \le 4$ </li> </ol> <strong>Exercise 4:</strong> A person pays $1 for a lottery ticket. They'll gain $50 if winning with the likelihood of $\frac{1}{100}$; otherwise, nothing. If they do this for a while, would they gain and lose money? How much percentage? <br/> <strong>Exercise 5:</strong> What is variance of shooting a board if the probability of shooting inside is 0.8? The mean is also 0.8. <br/> <strong>Exercise 6:</strong> What is variance of $-\sqrt{6}, -\sqrt{5}, -\sqrt{4}. -1, 0, 1, \sqrt{4}, \sqrt{5}, \sqrt{6}$? <br/> <strong>Exercise 7:</strong> What is variance of a constant? <br/> <strong>Exercise 8:</strong> Prove the properties of expected values. <br/> <strong>Exercise 9:</strong> Prove the third equality in the variance formula. <br/> <strong>Exercise 10:</strong> Throw a die. Consider the number on each side is a random variable of this experiment. Compute the expected value, variance, and standard derivation. <br/> <strong>Exercise 11:</strong> Write programs for exercises when applicable using <code>numpy</code>. </div> </details> <hr/> <h2 id="covariance-and-correlation"><strong>Covariance and Correlation</strong></h2> <p>:question: We’ve been dealing with cases with only one random variable. <strong>What if we’d like to know the relationship between two random variables?</strong></p> <p>This is where <em>covariance</em> and <em>correlation</em> come into play.</p> <h3 id="covariance"><strong>Covariance</strong></h3> <p><strong>Covariance</strong> measures the linear relationship of two random variables, that is, how they change together. Recall $n - 1$ is for samples as we consider $X$ and $Y$ as subset of the population. Use <code class="language-plaintext highlighter-rouge">np.cov()</code>.</p> \[cov(X, Y) = E[(X - \mu_X)(Y - \mu_Y)] = \frac{\sum(x - \mu_X)(y - \mu_Y)}{n - 1}\] <p>where $X$ and $Y$ are random variables such that $E[X] = \mu_X$ and $E[Y] = \mu_Y$.</p> <p>We deduce from the above formula that $var(X) = cov(X, X)$. <em>Formally</em>, <strong>covariance</strong> is represented using the following 2-by-2 matrix</p> \[\begin{bmatrix} var(X) &amp; cov(Y, X) \\ cov(X, Y) &amp; var(Y) \end{bmatrix}.\] <p>We can interpret the result as follows.</p> <ul> <li>$cov(X, Y) \in (-\infty, \infty)$.</li> <li>$cov(X, Y) = cov(Y, X)$.</li> <li>If it is positive covariance or $cov(X, Y) &gt; 0$, then $X$ <em>increases</em> implies $Y$’s <em>rise</em> on average.</li> <li>If it is negative covariance or $cov(X, Y) &lt; 0$, then $X$ <em>reduces</em> implies $Y$’s <em>decrease</em> on average.</li> <li>If it is zero covariance or $cov(X, Y) = 0$, then there is <em>no</em> linear correlation between two random variables.</li> </ul> <p><del>DEMONATRATION</del></p> <h3 id="correlation"><strong>Correlation</strong></h3> <p>Even though covariance is useful, its value can be unnecessarily large. In addition, covariance only indicates the direction of the relationship, <em>not</em> how much it is. To address this issue, we standardize the covariance to obtain the <strong>correlation coefficient</strong>.</p> <p><strong>Correlation</strong> is a statistical measure the quantifies the strength and direction of a <em>linear relationship</em> between two random variables. Use <code class="language-plaintext highlighter-rouge">np.corrcoef()</code>.</p> \[\begin{align*} corr(X, Y) &amp; = \frac{cov(X, Y)}{\sigma_X \cdot \sigma_Y} = \frac{cov(X, Y)}{\sqrt{var(X)} \cdot \sqrt{var(Y)}} \\ &amp; = \frac{n (\sum_{i} x_i y_i) - (\sum_i x_i) (\sum_i y_i)}{\sqrt{n (\sum_{i} x_i^2) - (\sum_{i} x_i)^2} \cdot \sqrt{n (\sum_{i} y_i^2) - (\sum_{i} y_i)^2}} \end{align*}\] <p>It is also often represented as a 2-by-2 matrix like covariance.</p> \[\begin{equation*} \begin{bmatrix} corr(X, X) &amp; corr(Y, X) \\ corr(X, Y) &amp; corr(Y, Y) \end{bmatrix} = \begin{bmatrix} 1 &amp; corr(Y, X) \\ corr(X, Y) &amp; 1 \end{bmatrix} \end{equation*}\] <p>We can interpret the result as follows.</p> <ul> <li>$corr(X, Y) \in [-1, 1]$.</li> <li>$corr(X, Y) = corr(Y, X)$.</li> <li>If it is positive correlation or $corr(X, Y) &gt; 0$, we have as $X$ increases, $Y$’s rise on average. <ul> <li>If $corr(X, Y) = 1$, values of $X$ and $Y$ form a straight line with a positive slope.</li> <li>If $corr(X, Y)$ is close to 1, then it is a <em>strong</em> relationship and values of $X$ and $Y$ lie close to a straight line.</li> <li>If $corr(X, Y)$ is not so close to 1, then it is a <em>weaker</em> relationship and values of $X$ and $Y$ are distributed around but further away from a straight line.</li> </ul> </li> <li>If it is negative covariance or $corr(X, Y) &lt; 0$, we have as $X$ reduces, $Y$’s decrease on average. <ul> <li>If $corr(X, Y) = -1$, values of $X$ and $Y$ form a straight line with a negative slope.</li> <li>If $corr(X, Y)$ is close to -1, then it is a <em>strong</em> relationship and values of $X$ and $Y$ lie close to a straight line.</li> <li>If $corr(X, Y)$ is not so close to -1, then it is a <em>weaker</em> relationship and values of $X$ and $Y$ are distributed around but further away from a straight line.</li> </ul> </li> <li>If it is zero covariance or $corr(X, Y) = 0$, then there is <em>no</em> linear relationship between two random variables and values scatter without a clear linear pattern.</li> </ul> <p><del>DEMONATRATION</del></p> <h3 id="continuous-random-variable-1"><strong>Continuous Random Variable</strong></h3> <p>Like mathematical expectations, we <em>cannot</em> directly compute covariance and correlation of continuous random variables. In this case, we can convert continous random variables to discrete ones using <strong>Monte Carlo method</strong>. Softwares may have it set up in methods as follows.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># X ~ N(0, 1) 
# Y ~ N(0.5, 1.5)
</span><span class="n">data1</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="nf">normal</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1000</span><span class="p">)</span>
<span class="n">data2</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="nf">normal</span><span class="p">(</span><span class="mf">0.5</span><span class="p">,</span> <span class="mf">1.5</span><span class="p">,</span> <span class="mi">1000</span><span class="p">)</span>

<span class="c1"># calculate covariance and correlation matrix
</span><span class="n">covariance_matrix</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">cov</span><span class="p">(</span><span class="n">data1</span><span class="p">,</span> <span class="n">data2</span><span class="p">)</span>
<span class="n">correlation_matrix</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">corrcoef</span><span class="p">(</span><span class="n">data1</span><span class="p">,</span> <span class="n">data2</span><span class="p">)</span>
</code></pre></div></div> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> Compute their covariance and correlation for X and Y below. Determine their relationship in detail. <ol> <li> $X = \\{1, 3, 4, 4\\}$ and $Y = \\{1, 2, 3, 2\\}$ </li> <li> $X = \\{1, 1, 2, 4\\}$ and $Y = \\{2, 3, 4, 3\\}$ </li> </ol> <strong>Exercise 2:</strong> Prove the third equality of correlation formula. <br/> <strong>Exercise 3:</strong> Prove that $corr(X, Y) \in [-1, 1]$. <br/> <strong>Exercise 4:</strong> Prove that $corr(X, Y) = \pm 1$ implies values of X and Y are on a straight line. <br/> <strong>Exercise 5:</strong> Write programs for exercises when applicable using <code>numpy</code>. </div> </details> <p><br/></p> <hr/> <h2 id="speech_balloon-faq">:speech_balloon: <strong>FAQ</strong></h2> <p>Here are some good questions asked during class. The answers to these questions came from either some admin, TA, STA, or members of AIO2025.</p> <table> <thead> <tr> <th>Questions</th> <th>Answers</th> </tr> </thead> <tbody> <tr> <td>Are there any cases where continuous random variables are converted to discrete random variables for easier computations?</td> <td>Yes. We can use <strong>Monte Carlo method</strong> to make it happen. We divide uncountable values into bins/classes/intervals which are considered single values, then use naive Bayes or histogram. For example, salaries can be categorized into groups like low, medium, and high.</td> </tr> <tr> <td>Resources for Applied Statistics</td> <td><a href="https://www.google.com/books/edition/Storytelling_with_Data/rRSRCgAAQBAJ?hl=en&amp;gbpv=0">Storytelling with Data: A Data Visualization Guide for Business Professionals</a> by Cole Nussbaumer Knaflic, <a href="https://www.amazon.com/Statistics-Science-Learning-Books-Carte/dp/0133860825">Statistics: The Art and Science of Learning from Data</a> by Alan Agresti, <a href="https://www.amazon.com/Introduction-Probability-Chapman-Statistical-Science/dp/1138369918">Introduction to Probability</a> by Blitzstein</td> </tr> <tr> <td>How do Riemman and Lebesgue integrals work?</td> <td>Riemman integral is like accounting, accummulating bit by bit. Lebesgue integral is like a cashier taking money, putting money into their own category and summing them up in the end.</td> </tr> <tr> <td>Why n - 1 for sample?</td> <td>This is called Bessel’s correction in Statistics. When using sample, selected value points tend to lie closer to the sample mean than the population mean. Thus, using n makes values’ distribution (variance/standard deviation) is lower than what it really is. Taking n - 1 is a way to make up for it to address bias, helping the result reflect the distribution of the population better. When writing code, use <code class="language-plaintext highlighter-rouge">ddof = 1</code> for variance and standard deviation computations.</td> </tr> <tr> <td>How are these expectations used in Machine Learning?</td> <td>They can be used in optimization problems. In some cases, models need to take actions that maximize expectations in game theory. Basically, “learning” in Machine Learning is to find models so that average expectation errors are the least insignificant.</td> </tr> </tbody> </table> <p><br/></p> ]]></content><author><name></name></author><category term="AI"/><category term="coding"/><category term="AIO"/><category term="prob-stat"/><summary type="html"><![CDATA[random variable, expectation, variance, standard deviation, covariance, correlation]]></summary></entry><entry><title type="html">Git &amp;amp; GitHub for Version Control</title><link href="https://huong-vo.github.io/blog/2025/git-github/" rel="alternate" type="text/html" title="Git &amp;amp; GitHub for Version Control"/><published>2025-07-14T00:00:00+00:00</published><updated>2025-07-14T00:00:00+00:00</updated><id>https://huong-vo.github.io/blog/2025/git-github</id><content type="html" xml:base="https://huong-vo.github.io/blog/2025/git-github/"><![CDATA[<p><strong>Acknowledgement:</strong> This post is based mainly on Dr. Thai-Ha Nguyen’s presentation and slides and partly on on TA, STA, and peers’ responses from the course AIO2025 offered by AI VIETNAM.</p> <p><strong>Relevance:</strong> The audience of this lecture were those who have had some coding experience. Absolute beginners are encouraged to read the material and repeatedly practice coding without AI assistance to strengthen their coding and code-reading skills. Note that only necessary Python knowledge is introduced here.</p> <p><strong>Relevance:</strong> The audience of this lecture were those who could have known nothing about database or have had some experience working with data. Absolute beginners are encouraged to read and practice the basics without AI assistance to familiarize themselves with the material. Note that only necessary SQL knowledge is introduced here.</p> <blockquote> <p>Always understand the principles first then practice. <br/> -Dr. Thai-Ha Nguyen</p> </blockquote> <p>In today’s post, we’ll explore Git and GitHub. They are essential tools any developer or engineer should be familiar with. We can use them for personal and collaborative purposes.</p> <p>Let’s implant some analogies of Git and GitHub in mind before delving into both. Git is like a camera capturing snapshots of a project continuously. GitHub is like Google Photos storing all photos from Git to cloud so that we can review and share with anyone at any point in time.</p> <hr/> <h2 id="git-principles-and-configuration"><strong>Git Principles and Configuration</strong></h2> <h3 id="version-control-system-vcs"><strong>Version Control System (VCS)</strong></h3> <p>VCS is a general term for Git. VCS is a system that tracks and saves changes of files over time. It contains all information about</p> <ul> <li>what has been changed</li> <li>who has changed it</li> <li>when it was changed</li> <li>why it was changed.</li> </ul> <p>With these properties, this system enables us to</p> <ul> <li>revert to previous working state when bugs/errors/mistakes have been made</li> <li>compare the difference between two versions</li> <li>show related change information</li> <li>protect data and support collaboration.</li> </ul> <p>There are three types of VCS:</p> <ol> <li> <p><strong>Local VCS</strong> is the very first VCS for one-person use. That is, everything is saved locally in a computer, so there is no way to recover if the computer quits working and it’s difficult to share your work with others.</p> </li> <li> <p><strong>Centralized VCS (CVCS)</strong>, for example Subversion and CVS, is another old VCS where developers use one single server to store their work. Even though it facilitates seamless collaboration, it totally depends on the centralized server. This would be catastrophic if the server corrupted. Users wouldn’t be able to save versioned changes or collaborate with others.</p> </li> <li> <p><strong>Distributed VCS (DVCS)</strong>, for example Git and Mercurial, is the currently-used VCS where each developer has their own server. That is, every user has a local copy of complete projects and history at all times. If their server is inaccessible, they can always create a new one and share their work with others. Thus, it is complete, secure, and flexible.</p> </li> </ol> <h3 id="git"><strong>Git</strong></h3> <p>Git is a DVCS created by Linus Torvalds in 2005, who is the father of Linux. He designed Git to manage Linux source code since Linux is developed by thousands of people from all over the place. Git is widely adopted just a short while after its creation as it is simple, lightweight, instantaneous, and easy to branch and distribute.</p> <p><strong>SSO Principles</strong> <br/></p> <ol> <li><strong>Snapshot</strong> instead of diff <ul> <li>Git snaps an entire project after each change (when committing) to capture all changes at once.</li> <li>If there’s no change, Git creates a link to the old version to save storage capacity.</li> </ul> </li> <li><strong>SHA-1 Data Integrity</strong> <ul> <li>Git protects data by the SHA-1 function with unique identifiers—40-character hash codes like ID (for each commit).</li> <li>when content alters, so does its hash code, which ensures commit integrity.</li> </ul> </li> <li><strong>Offline-first</strong> <ul> <li>Git allows us to work and save your progress offline (commit).</li> <li>We only need the Internet when pull/push.</li> </ul> </li> </ol> <p><strong>Configuration</strong> <br/> After installation, some basic configurations can be done for Git are</p> <ul> <li>git config –global user.name your_name:</li> <li>git config –global user.email your_email</li> <li>git config –global core.editor “code-wait”</li> <li>git config –global color.ui auto</li> </ul> <p>They are all automatically imported into a file on our computer. We can use git config –list to see them.</p> <p>An alias for a command statement is very useful while we work with Git. It is to replace a long word/phrase with a short user-defined name.</p> <p>git log shows history of your work which may be prolix. We can customize the output so that it is clear, concise, and pretty.</p> <p>These enables us to work faster and more efficiently.</p> <p>git –init</p> <hr/> <h2 id="basic-git"><strong>Basic Git</strong></h2> <p>A repository acts like a folder and journal.</p> <h3 id="repository"><strong>Repository</strong></h3> <p>locally mkdir repo_name</p> <p>cd repo_name</p> <p>git init initiates a hidden file named .git</p> <p>ls -la list all files including hidden files</p> <p>clone from GitHub-the server .gitignore contained files that we don’t want to publish online</p> <p>license: how to cite</p> <p>click and press . to open file on github -&gt; git dev</p> <p>echo “message” » README.md -&gt; create readme git branch -M main</p> <p>clone ssh -&gt; more secure git clone “https://…” git clone ssh_code</p> <p>echo “message” » sample.txt -&gt; create a file echo “another message” » sample.txt -&gt; add more message</p> <p>primary process to work with git</p> <p>git status git add file_name git add . git commit -m “message” git push -u origin main</p> <p>git log -&gt; too long what if there are thousands of lines git log –oneline -&gt; don’t know who changed what git lg (customized)</p> <p>xem lich su commit: top line is main branch, bottom line is other branch</p> <p>commit should have meanings - like photo should show when and where</p> <p>git len extension: visualize commit</p> <p>tagging -&gt; milestone</p> <p>lightweight tag -&gt; in project, baseline or xgb</p> <h4 id="__________heading-3____________"><strong>__________Heading 3____________</strong></h4> <p>branch: if all A B C commit to main -&gt; unorganized and hard to managed -&gt; not commit to main -&gt; instead commit to a branch for each feature developed main should always be ready to deploy (deployable)</p> <p>git branch -M main -&gt; see all branches git branch branch_name -&gt; create a branch (ex, feature/login) git checkout branch_name -&gt; go to that branch -&gt; all commits after go to this branch git checkout -b branch_name -&gt; create and go to that branch</p> <p>merge all branches to main -&gt; 2 ways</p> <ol> <li>used less git checkout main git merge branch_name</li> <li>more common git push -u origin branch_name on github: compare and pull request create pull request -&gt; send request to all teams so reviewer can check if code is good to go reviewer: once good -&gt; merge pull request -&gt; confirm merge</li> </ol> <p>file conflict: -&gt; in exercise A and B change the same file but B does better -&gt; choose B remove A -&gt; solve conflict by ?</p> <p>once a project is done, all branches are merged to main -&gt; should erase all branches to eliminate redundancy</p> <p>rebase: more linear merge: parallel</p> <p><strong>Concept Name</strong> <br/> command to link local repo to remote repo -&gt; git remote add origin url command to get info of all branches from remote repo to local without combining -&gt; git fetch origin process to create and push code to remote git init -&gt; git add . -&gt; git commit -m message -&gt; git remote add origin url -&gt; git push origin main or git init -&gt; git remote add origin url -&gt; git add . -&gt; git commit -m message -&gt; git push origin main a file created in branch main, not added to staged, not committed yet -&gt; what happens when moving to a different branch? file on new branch but not ffollowed in video exercise</p> <h3 id="__________heading-2____________"><strong>__________Heading 2____________</strong></h3> <h4 id="__________heading-3____________-1"><strong>__________Heading 3____________</strong></h4> <p><strong>Concept Name</strong> <br/></p> <h3 id="__________heading-2____________-1"><strong>__________Heading 2____________</strong></h3> <h4 id="__________heading-3____________-2"><strong>__________Heading 3____________</strong></h4> <p><strong>Concept Name</strong> <br/></p> <hr/> <h2 id="intermediate-git"><strong>Intermediate Git</strong></h2> <h3 id="__________heading-2____________-2"><strong>__________Heading 2____________</strong></h3> <h4 id="__________heading-3____________-3"><strong>__________Heading 3____________</strong></h4> <p><strong>Concept Name</strong> <br/></p> <h3 id="__________heading-2____________-3"><strong>__________Heading 2____________</strong></h3> <h4 id="__________heading-3____________-4"><strong>__________Heading 3____________</strong></h4> <p><strong>Concept Name</strong> <br/></p> <h3 id="__________heading-2____________-4"><strong>__________Heading 2____________</strong></h3> <h4 id="__________heading-3____________-5"><strong>__________Heading 3____________</strong></h4> <p><strong>Concept Name</strong> <br/></p> <hr/> <h2 id="collaborate-via-github"><strong>Collaborate via GitHub</strong></h2> <h3 id="__________heading-2____________-5"><strong>__________Heading 2____________</strong></h3> <h4 id="__________heading-3____________-6"><strong>__________Heading 3____________</strong></h4> <p><strong>Concept Name</strong> <br/></p> <h3 id="__________heading-2____________-6"><strong>__________Heading 2____________</strong></h3> <h4 id="__________heading-3____________-7"><strong>__________Heading 3____________</strong></h4> <p><strong>Concept Name</strong> <br/></p> <h3 id="__________heading-2____________-7"><strong>__________Heading 2____________</strong></h3> <h4 id="__________heading-3____________-8"><strong>__________Heading 3____________</strong></h4> <p><strong>Concept Name</strong> <br/></p> <hr/> <h2 id="advanced-git"><strong>Advanced Git</strong></h2> <h3 id="__________heading-2____________-8"><strong>__________Heading 2____________</strong></h3> <h4 id="__________heading-3____________-9"><strong>__________Heading 3____________</strong></h4> <p><strong>Concept Name</strong> <br/></p> <h3 id="__________heading-2____________-9"><strong>__________Heading 2____________</strong></h3> <h4 id="__________heading-3____________-10"><strong>__________Heading 3____________</strong></h4> <p><strong>Concept Name</strong> <br/></p> <h3 id="__________heading-2____________-10"><strong>__________Heading 2____________</strong></h3> <h4 id="__________heading-3____________-11"><strong>__________Heading 3____________</strong></h4> <p><strong>Concept Name</strong> <br/></p> <table> <thead> <tr> <th style="text-align: center">Header Name 1</th> <th style="text-align: center">Header Name 2</th> <th style="text-align: center">Header Name 3</th> </tr> </thead> <tbody> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> <tr> <td style="text-align: center">column content 1</td> <td style="text-align: center">column content 2</td> <td style="text-align: center">column content 3</td> </tr> </tbody> </table> <p><br/></p> <p><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>TABLE ONLY</strong>__________________________________________________________________</p> <table class="language-python"> <tr> <th>Header name 1</th> <th>Header name 2</th> <th>Header name 3</th> </tr> <tr> <td> <strong>Row content 1</strong> <ul> <li> ______________________________________________ </li> <li> ______________________________________________ </li> </ul> </td> <td> <pre>
<code class="language-python">
# _________________explanation_________________

_________________setup_________________

# test
_________________code_________________

'''
_________________rendered output_________________
'''
</code>
</pre> </td> <td> <pre>
<code class="language-python">
# _________________explanation_________________

_________________setup_________________

# test
_________________code_________________

'''
_________________rendered output_________________
'''
</code>
</pre> </td> </tr> </table> <p><br/></p> <p><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>COLLAPSIBLE BLOCK WITH TABLE INSIDE</strong>__________________________________________________________________</p> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="inside-bordered-block language-python"> <strong>Exercise 1:</strong> _____________________________________________________ <br/> <strong>Exercise 2:</strong> _____________________________________________________ <table class="language-python"> <tr> <th>Header name 1</th> <th>Header name 2</th> <th>Header name 3</th> </tr> <tr> <td> <strong>Row content 1</strong> <ul> <li> ______________________________________________ </li> <li> ______________________________________________ </li> </ul> </td> <td> <pre>
<code class="language-python">
# _________________explanation_________________

_________________setup_________________

# test
_________________code_________________

'''
_________________rendered output_________________
'''
</code>
</pre> </td> <td> <pre>
<code class="language-python">
# _________________explanation_________________

_________________setup_________________

# test
_________________code_________________

'''
_________________rendered output_________________
'''
</code>
</pre> </td> </tr> </table> <br/> <strong>Exercise 3:</strong> _____________________________________________________ </div> </details> <p><br/></p> <p><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>COLLAPSIBLE BLOCK WITH EXERCISES ONLY</strong>__________________________________________________________________</p> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> ____________________________________________________________________________ <br/> <strong>Exercise 2:</strong> ____________________________________________________________________________ <br/> <strong>Hints:</strong> ____________________________________________________________________________ <br/> <strong>Exercise 3:</strong> ____________________________________________________________________________ <ul> <li> ______________________________________________ </li> <li> ______________________________________________ </li> </ul> <strong>Exercise 4:</strong> ____________________________________________________________________________ <ol> <li> ______________________________________________ </li> <li> ______________________________________________ </li> </ol> </div> </details> <p><br/></p> <p><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>COLLAPSIBLE BLOCK WITH EXERCISES AND CODE</strong>__________________________________________________________________</p> <details class="bordered-block"> <summary> <strong>Practice Exercises</strong> </summary> <div class="bordered-inner-block language-python"> <strong>Exercise 1:</strong> ____________________________________________________________________________ <pre>
<code class="language-python">
_________________code_________________
</code>
</pre> <strong>Exercise 2:</strong> ____________________________________________________________________________ <br/> <strong>Exercise 3:</strong> ____________________________________________________________________________ <ol> <li> ______________________________________________ </li> <li> ______________________________________________ </li> </ol> <strong>Exercise 4:</strong> ____________________________________________________________________________ <ul> <li> ______________________________________________ </li> <li> ______________________________________________ </li> </ul> </div> </details> <p><br/></p> <ol> <li><strong>______<em>main idea</em>________:</strong> <strong>__</strong><strong>__</strong><strong>__</strong><strong>__content - explanation</strong>_______________________</li> <li><strong>______<em>main idea</em>________:</strong> <strong>__</strong><strong>__</strong><strong>__</strong><strong>__content - explanation</strong>_______________________</li> <li><strong>______<em>main idea</em>________:</strong> <strong>__</strong><strong>__</strong><strong>__</strong><strong>__content - explanation</strong>_______________________ <ul> <li><strong>______<em>main idea</em>________:</strong> <strong>__</strong><strong>__</strong><strong>__</strong><strong>__content - explanation</strong>_______________________</li> <li><strong>______<em>main idea</em>________:</strong> <strong>__</strong><strong>__</strong><strong>__</strong><strong>__content - explanation</strong>_______________________</li> <li><strong>______<em>main idea</em>________:</strong> <strong>__</strong><strong>__</strong><strong>__</strong><strong>__content - explanation</strong>_______________________</li> </ul> </li> </ol> <ul> <li><strong>______<em>main idea</em>________:</strong> <strong>__</strong><strong>__</strong><strong>__</strong><strong>__content - explanation</strong>_______________________</li> <li><strong>______<em>main idea</em>________:</strong> <strong>__</strong><strong>__</strong><strong>__</strong><strong>__content - explanation</strong>_______________________</li> <li><strong>______<em>main idea</em>________:</strong> <strong>__</strong><strong>__</strong><strong>__</strong><strong>__content - explanation</strong>_______________________</li> </ul> <p>:bulb: <strong>Applications in AI</strong></p> <p>:pushpin: <strong>Pro tip:</strong> <strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong></p> <p>:warning: <strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong></p> <p>:heavy_exclamation mark: <strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong><strong>__</strong></p> <p>:question: <strong>__________________________________________________________________?</strong></p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">__________________________________________________</span>

<span class="o">&gt;&gt;&gt;</span>
</code></pre></div></div> <p>❌ ✅ ⚠️</p> <hr/> <h2 id="speech_balloon-faq">:speech_balloon: <strong>FAQ</strong></h2> <p>Here are some good questions asked during class. The answers to these questions came from either some admin, TA, STA, or members of AIO2025.</p> <table> <thead> <tr> <th>Questions</th> <th>Answers</th> </tr> </thead> <tbody> <tr> <td>Question</td> <td>Answer</td> </tr> <tr> <td>Question</td> <td>Answer</td> </tr> <tr> <td>Question</td> <td>Answer</td> </tr> <tr> <td>Question</td> <td>Answer</td> </tr> <tr> <td>Question</td> <td>Answer</td> </tr> <tr> <td>Question</td> <td>Answer</td> </tr> <tr> <td>Question</td> <td>Answer</td> </tr> <tr> <td>Question</td> <td>Answer</td> </tr> <tr> <td>Question</td> <td>Answer</td> </tr> <tr> <td>Question</td> <td>Answer</td> </tr> <tr> <td>Question</td> <td>Answer</td> </tr> </tbody> </table> <p><br/></p> <h4 id="check-list">Check List</h4> <ul class="task-list"> <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked"/>Exercise 1 (date)</li> <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled"/>Exercise 2 (date) <ul class="task-list"> <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked"/>Put on left sock</li> <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled"/>Put on right sock</li> </ul> </li> <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked"/>Go to school</li> </ul>]]></content><author><name></name></author><category term="AI"/><category term="coding"/><category term="AIO"/><summary type="html"><![CDATA[git, github]]></summary></entry></feed>