Skip to content
Laviyx Learn

Try

Clustering, PCA, Text and Production Practice

Practise k-means, PCA, anomaly detection, TF-IDF text classification, cosine similarity, saving models and drift checks.

Intermediate7 problemsUpdated

Review first: k-Means Clustering, Principal Component Analysis (PCA), Anomaly Detection, Text Classification, Embeddings and Semantic Search, Saving and Loading Models, MLOps: Models in Production

  1. 1.Cluster centres

    Intermediate

    Cluster the points [[1, 2], [1.5, 1.8], [1, 1], [8, 8], [9, 9.5], [8.5, 9]] into 2 groups with KMeans(n_clusters=2, n_init=10, random_state=0). Print the labels and the cluster centres rounded to 2 decimals, sorted by their first coordinate.

    Show hint

    kmeans.labels_ and kmeans.cluster_centers_; sort the centres with sorted(..., key=lambda c: c[0]).

    Show solution
    Python
    import numpy as np
    from sklearn.cluster import KMeans
    
    points = np.array([[1, 2], [1.5, 1.8], [1, 1], [8, 8], [9, 9.5], [8.5, 9]])
    kmeans = KMeans(n_clusters=2, n_init=10, random_state=0).fit(points)
    print(kmeans.labels_)
    print(sorted(kmeans.cluster_centers_.round(2).tolist(), key=lambda c: c[0]))
    Output
    [1 1 1 0 0 0]
    [[1.17, 1.6], [8.5, 8.83]]
  2. 2.Best k by silhouette

    Intermediate

    Generate data with make_blobs(n_samples=500, centers=4, random_state=2). For k from 2 to 6, fit k-means (n_init=10, random_state=0) and print k with its silhouette score (3 decimals). Which k scores highest?

    Show hint

    silhouette_score(X, km.labels_) from sklearn.metrics.

    Show solution
    Python
    from sklearn.cluster import KMeans
    from sklearn.datasets import make_blobs
    from sklearn.metrics import silhouette_score
    
    X, _ = make_blobs(n_samples=500, centers=4, random_state=2)
    for k in range(2, 7):
        km = KMeans(n_clusters=k, n_init=10, random_state=0).fit(X)
        print(k, round(silhouette_score(X, km.labels_), 3))
    Output
    2 0.556
    3 0.66
    4 0.625
    5 0.567
    6 0.506

    The data was generated with 4 blobs, yet k = 3 scores highest: two of the blobs lie close together, so treating them as one cluster gives better separation. The silhouette score measures separation, not the true number of groups.

  3. 3.Components for 90% variance

    Intermediate

    How many principal components keep 90% of the variance of the scaled digits data (load_digits)? Print the number and the exact share of variance they keep (3 decimals).

    Show hint

    PCA(n_components=0.9); then n_components_ and explained_variance_ratio_.sum().

    Show solution
    Python
    from sklearn.datasets import load_digits
    from sklearn.decomposition import PCA
    from sklearn.preprocessing import StandardScaler
    
    X = StandardScaler().fit_transform(load_digits().data)
    pca = PCA(n_components=0.9).fit(X)
    print(pca.n_components_, round(pca.explained_variance_ratio_.sum(), 3))
    Output
    31 0.9
  4. 4.Unusual transactions

    Intermediate

    Transaction amounts are normally around ₹1,500 ± 300. Simulate 400 normal amounts (rng = np.random.default_rng(5)), append [9000, 50, 7500], fit IsolationForest(contamination=0.01, random_state=0) on the amounts as one column, and print the flagged amounts rounded to whole rupees, sorted.

    Show hint

    Reshape with .reshape(-1, 1); predict returns -1 for flagged rows.

    Show solution
    Python
    import numpy as np
    from sklearn.ensemble import IsolationForest
    
    rng = np.random.default_rng(5)
    amounts = np.r_[rng.normal(1500, 300, 400), [9000, 50, 7500]].reshape(-1, 1)
    flags = IsolationForest(contamination=0.01, random_state=0).fit_predict(amounts)
    print(np.sort(amounts[flags == -1].ravel()).round(0))
    Output
    [  50. 2308. 2332. 7500. 9000.]

    With 403 amounts, a contamination of 1% flags 5 of them: the three planted amounts plus the two largest normal ones. contamination sets how many points are flagged, so ranking by score is usually more useful than a fixed rate.

  5. 5.Review sentiment

    Intermediate

    Train a TF-IDF + logistic regression pipeline on these reviews (1 = positive) and predict the labels of "great food" and "slow and cold".

    Python
    reviews = ["great food and friendly staff", "food was cold and bland", "loved the great service",
               "very slow service", "friendly and fast", "cold, slow and rude"]
    labels = [1, 0, 1, 0, 1, 0]
    Show hint

    make_pipeline(TfidfVectorizer(), LogisticRegression()).fit(reviews, labels).

    Show solution
    Python
    from sklearn.feature_extraction.text import TfidfVectorizer
    from sklearn.linear_model import LogisticRegression
    from sklearn.pipeline import make_pipeline
    
    reviews = ["great food and friendly staff", "food was cold and bland", "loved the great service",
               "very slow service", "friendly and fast", "cold, slow and rude"]
    labels = [1, 0, 1, 0, 1, 0]
    model = make_pipeline(TfidfVectorizer(), LogisticRegression()).fit(reviews, labels)
    print(model.predict(["great food", "slow and cold"]))
    Output
    [1 0]
  6. 6.Most similar document

    Intermediate

    Represent these documents with TF-IDF and find which one is most similar (by cosine similarity) to the query "refund for my broken phone". Print the document and the similarity (3 decimals).

    Python
    docs = ["phone screen broken, need repair", "how to get a refund", "refund for a broken phone charger", "delivery is late"]
    Show hint

    Fit the vectoriser on docs, transform the query, and use cosine_similarity from sklearn.metrics.pairwise.

    Show solution
    Python
    from sklearn.feature_extraction.text import TfidfVectorizer
    from sklearn.metrics.pairwise import cosine_similarity
    
    docs = ["phone screen broken, need repair", "how to get a refund", "refund for a broken phone charger", "delivery is late"]
    tfidf = TfidfVectorizer().fit(docs)
    scores = cosine_similarity(tfidf.transform(["refund for my broken phone"]), tfidf.transform(docs))[0]
    best = scores.argmax()
    print(docs[best], round(scores[best], 3))
    Output
    refund for a broken phone charger 0.861
  7. 7.Check features for drift

    Intermediate

    With rng = np.random.default_rng(3), simulate training data for two features (clicks: normal mean 20, sd 5, 2000 rows; basket: normal mean 800, sd 150, 2000 rows) and recent data (clicks unchanged with 500 rows; basket mean 900, sd 150, 500 rows). For each feature print the KS statistic (3 decimals) and whether the p-value is below 0.01.

    Show hint

    ks_2samp(train, recent) from scipy.stats returns statistic and pvalue.

    Show solution
    Python
    import numpy as np
    from scipy.stats import ks_2samp
    
    rng = np.random.default_rng(3)
    train = {"clicks": rng.normal(20, 5, 2000), "basket": rng.normal(800, 150, 2000)}
    recent = {"clicks": rng.normal(20, 5, 500), "basket": rng.normal(900, 150, 500)}
    for name in train:
        result = ks_2samp(train[name], recent[name])
        print(name, round(result.statistic, 3), bool(result.pvalue < 0.01))
    Output
    clicks 0.056 False
    basket 0.229 True

    Only basket has shifted (mean 800 to 900). The clicks feature has a small KS statistic and its p-value stays above 0.01, as expected for data from the same distribution.