BuildlyBuildly
DashboardExercisesProfileHelp

Data Science

Drill the skill in short exercises, then apply it in a real-world scenario. Each module ladders up from practice to application.

Practice by skill

28 skills
NumPy Arrays
0/3
Dates & Times
0/3
Group & Aggregate
0/3
Merge & Concat
0/3
Pivot & Reshape
0/3
Series & DataFrame
0/3
String Cleanup (Series)
0/3
Conditionals
0/3
Functions
0/3
Lists
0/3
Sets & Tuples
0/3
Matplotlib Basics
0/3
Subplots
0/3
Seaborn Distributions
0/3
CSV Cleanup
0/3
Filtering
0/3
CSV I/O (basic)
0/3
Missing Data
0/3

Module 1: Python Programming

0/26 done
Exercises (0/24)
  • 1
    Rectangle AreaUp next
    Write rectangle_area(width, height) that returns the area of a rectangle.
    5 min· 10 XP
  • 2
    Voting Age Check
    Write can_vote(age) that returns True if the person is 18 or older, else False. Write it as a single comparison — no if/else needed (that comes later).
    5 min· 10 XP
  • 3
Selection & Indexing
0/3
Sort & Rank
0/3
Transform Columns
0/3
Dictionaries
0/3
Python Fundamentals
0/3
Loops
0/3
Strings
0/3
Matplotlib Customization
0/3
Seaborn Categorical
0/3
Seaborn Relationships
0/3
Seconds to Minutes
Write seconds_to_minutes(total) that converts a total number of seconds into a string like "4m8s" — whole minutes plus leftover seconds. Use // for the integer-divided minutes and % for the leftover seconds, then stitch them into a string.
5 min· 10 XP
  • 4
    Title Case a Sentence
    Write to_titlecase(s) that returns s with the first letter of each word capitalized and the rest lowercase. Words are separated by single spaces.
    5 min· 10 XP
  • 5
    Extract Initials
    Write initials(name) that returns the initials of a name like "A.B.C.". Names are space-separated.
    5 min· 10 XP
  • 6
    Clean a Social Handle
    Write clean_handle(s) that normalizes a username: strip leading/trailing whitespace, remove any @, lowercase, and replace spaces with underscores.
    5 min· 10 XP
  • 7
    Sum the Even Numbers
    Write sum_evens(numbers) that returns the sum of the even numbers in a list. Empty list → 0.
    5 min· 10 XP
  • 8
    Find the Maximum
    Write find_max(numbers) that returns the largest number in the list. If the list is empty, return None.
    5 min· 10 XP
  • 9
    Remove Duplicates (Preserve Order)
    Write remove_duplicates(items) that returns a new list with duplicates removed, keeping the order of first appearance.
    5 min· 10 XP
  • 10
    Merge Vote Counts
    Write merge_counts(a, b) that takes two dicts of name → count and returns a new dict where each name's count is the sum across both inputs.
    5 min· 10 XP
  • 11
    Key With Highest Value
    Write top_key(d) that returns the key whose value is the largest. If d is empty, return None. If there's a tie, returning any one of the tied keys is fine.
    5 min· 10 XP
  • 12
    Letter Frequency
    Write letter_frequency(s) that returns a dict mapping each lowercase letter in s to how many times it appears. Treat s case-insensitively.
    5 min· 10 XP
  • 13
    Score to Letter Grade
    Write grade_letter(score) that returns a letter grade: A for ≥90, B for ≥80, C for ≥70, D for ≥60, otherwise F.
    5 min· 10 XP
  • 14
    Leap Year
    Write is_leap_year(year) that returns True if year is a leap year, else False. Rule: divisible by 4 AND (not divisible by 100 OR divisible by 400).
    5 min· 10 XP
  • 15
    BMI Category
    Write bmi_category(bmi) that returns: "Underweight" for BMI < 18.5, "Normal" for < 25, "Overweight" for < 30, "Obese" for ≥ 30.
    5 min· 10 XP
  • 16
    Count Vowels
    Write count_vowels(s) that counts how many vowels are in s (case-insensitive). Vowels are a, e, i, o, u.
    5 min· 10 XP
  • 17
    Multiplication Table
    Write multiplication_table(n, limit) that returns a list [n*1, n*2, ..., n*limit].
    5 min· 10 XP
  • 18
    FizzBuzz
    Write fizzbuzz(n) that returns a list of strings 1 through n. Replace multiples of 3 with "Fizz", multiples of 5 with "Buzz", multiples of both with "FizzBuzz". Other numbers are their string form.
    5 min· 10 XP
  • 19
    Customizable Greeting
    Write greet(name, greeting="Hello") that returns "{greeting}, {name}!". The second parameter has a default value.
    5 min· 10 XP
  • 20
    Clamp a Value
    Write clamp(value, low, high) that returns value if it's between low and high (inclusive), low if it's below, high if it's above.
    5 min· 10 XP
  • 21
    Discounted Price With Tax
    Write discounted_price(price, discount_percent=10, tax_percent=5) that applies the discount first, then adds tax on the discounted amount. Both percentages have defaults.
    5 min· 10 XP
  • 22
    Unique Words in a Sentence
    Write unique_words(sentence) that returns the unique (case-insensitive) words in sentence as a sorted list. Words are space-separated.
    5 min· 10 XP
  • 23
    Common Elements
    Write common_elements(a, b) that returns the items present in both lists, as a sorted list (case-sensitive, dedup any repeats).
    5 min· 10 XP
  • 24
    Min, Max, Average
    Write min_max_avg(numbers) that returns a list [min, max, avg] for a non-empty list. The average should be a float.
    5 min· 10 XP
  • Scenarios (0/2)
    Tempo Catalog Warmup
    You're an analyst at Tempo, a music streaming startup. The label pitch is Friday, and the team needs the prototype catalog sanity-checked before it ships.
    with Sarah·· 300 XP
    Skills you'll apply
    Conditionals
    0/3
    Dictionaries
    0/3
    Functions
    0/3
    Loops
    0/3
    Tally Statement Categorizer
    Capstone
    Tally’s first monthly statement ships Friday: users connect their bank, Tally pulls transactions, and tells them, “You spent ₹X this month, mostly on Y.” The feature depends on the categoriser.
    with Karan·· 400 XP
    Skills you'll apply
    Conditionals
    0/3
    Dictionaries
    0/3

    Module 2: Data Analytics

    0/33 done
    Exercises (0/30)
    • 1
      Celsius to Fahrenheit
      Context. Convert a list of Celsius temperatures to Fahrenheit in one shot — no loops. Your task. Write celsius_to_fahrenheit(celsius) that takes a list of Celsius numbers and returns a NumPy array of the matching Fahrenheit values. Formula: F = C * 9/5 + 32. Example. [0, 100] → array([32., 212.]). Notes. Use NumPy broadcasting — no for-loops. Empty input → empty array.
      7 min· 10 XP
    • 2
      Row Sums
      Context. A 2D table of weekly sales — each row is a product, each column a day of the week. You want the total per product. Your task. Write row_sums(matrix) that takes a 2D list (list of lists) and returns a NumPy array with one entry per row — the sum of that row. Example. [[1,2,3],[4,5,6]] → array([6, 15]). Notes. Use .sum() with the right axis argument. Rows with negative values still sum normally (no special-casing). A single-row input still returns a 1-element array.
      7 min· 10 XP
    • 3
      Pick Specific Columns
      Context. A wide sensor table — you only care about certain columns and want them in a specific order. Your task. Write pick_columns(matrix, cols) that takes a 2D list and a list of column indices, and returns a NumPy array with just those columns, in the order given. Example. matrix=[[1,2,3],[4,5,6]], cols=[0,2] → array([[1, 3], [4, 6]]). Notes. cols=[2,0] returns columns in that swapped order, not sorted. cols=[] returns a 2D array with 0 columns (one empty row per input row).
      7 min· 10 XP
    • 4
      Build a DataFrame from Lists
      Context. You collected student exam scores into two parallel lists and want them in a DataFrame for analysis. Your task. Write build_scores_df(names, scores) that returns a DataFrame with two columns, name and score, where each row pairs a name with its score (same index). Example. names=["Alice","Bob"], scores=[85,92] → | name | score | |-------|-------| | Alice | 85 | | Bob | 92 | Notes. Both inputs are always the same length. Empty inputs → empty DataFrame with both columns present.
      7 min· 10 XP
    • 5
      Shape and Column Names
      Context. Quick sanity check on a freshly loaded dataset — how big is it, and what are the columns called? Your task. Write describe_shape(df) that returns a dict with three keys: "rows" (number of rows), "columns" (number of columns), "column_names" (list of column names in their original order). Example. A 2×3 DataFrame with columns a, b, c → {"rows": 2, "columns": 3, "column_names": ["a", "b", "c"]}. Notes. Works on any DataFrame, including empty ones (0 rows but columns still defined).
      7 min· 10 XP
    • 6
      First N Rows
      Context. A preview helper — given a DataFrame and a count n, return only the first n rows. Your task. Write first_n(df, n) that returns a DataFrame containing the first n rows of df, preserving the original column order. Example. A 5-row df with n=2 → the first 2 rows as a DataFrame. Notes. If n is larger than the number of rows, return all rows. If n is 0, return an empty DataFrame (same columns, no rows).
      7 min· 10 XP
    • 7
      Parse a CSV String
      Context. A CSV string pulled from an API needs to be parsed into a DataFrame. Your task. Write parse_csv(csv_text) that takes a CSV-formatted string (header row present) and returns a DataFrame. Example. "a,b\n1,2\n3,4" → 2-row DataFrame with columns a and b. Notes. Use io.StringIO to wrap the string so pd.read_csv can read it like a file. Always has a header row.
      7 min· 10 XP
    • 8
      Serialize to a CSV String
      Context. After computing a report, ship it back as a CSV string (e.g., for download). Your task. Write to_csv_string(df) that returns the DataFrame serialized as a CSV string with header, without the index column. Example. A df with name/score → "name,score\nAlice,85\nBob,92\n". Notes. Include header. Skip the index column (otherwise pandas prepends an unnamed column with row numbers). Trailing newline is part of pandas' default output.
      7 min· 10 XP
    • 9
      Parse CSV and Filter
      Context. Parse a CSV of orders, then keep only the ones above a threshold amount. Your task. Write parse_and_filter(csv_text, min_amount) that parses the CSV and returns only rows where amount >= min_amount. Example. CSV of 4 orders with amounts [100, 30, 75, 10], min_amount=50 → rows with 100 and 75. Notes. Preserve row order from the CSV. min_amount higher than every value → empty DataFrame.
      7 min· 10 XP
    • 10
      Pick a Single Column
      Context. Extract a single column from a DataFrame for further analysis. Your task. Write get_scores(df) that returns the score column as a pandas Series (not a single-column DataFrame). Example. A df with name and score → the score Series. Notes. df["score"] returns a Series; df[["score"]] (double-bracket) would return a DataFrame — we want the first. Empty df → empty Series.
      7 min· 10 XP
    • 11
      Slice Rows by Position
      Context. Grab a positional slice of rows, regardless of the DataFrame's index. Your task. Write rows_by_position(df, start, stop) that returns rows from position start (inclusive) to stop (exclusive), using positional indexing. Example. A 5-row df with start=1, stop=3 → rows at positions 1 and 2. Notes. Same semantics as Python list slicing. start == stop → empty DataFrame. stop > len(df) → returns through end.
      7 min· 10 XP
    • 12
      Filter Rows by a Threshold
      Context. Keep only the rows whose score passes a threshold. Your task. Write rows_where_score_ge(df, threshold) that returns the rows of df where score >= threshold. Preserve original row order. Example. scores [90, 75, 85, 60], threshold=80 → rows with scores 90 and 85. Notes. Threshold higher than every score → empty DataFrame. Threshold lower than every score → all rows.
      7 min· 10 XP
    • 13
      Filter by Set Membership
      Context. Pull all orders from a specific set of regions. Your task. Write orders_in_regions(df, regions) that returns rows where df["region"] is in regions (a list of region codes). Example. regions ["A", "C"] → rows tagged A or C, in original order. Notes. Preserve original df order. Empty regions list → empty DataFrame.
      7 min· 10 XP
    • 14
      Filter by Numeric Range
      Context. Find rows in a numeric range — e.g., transactions between two amounts. Your task. Write amounts_between(df, low, high) that returns rows where df["amount"] is between low and high, inclusive on both ends. Example. amounts [10, 25, 50, 75, 100], low=25, high=75 → rows with 25, 50, 75. Notes. Both endpoints inclusive. low == high → only rows matching that exact value. Empty result if no values qualify.
      7 min· 10 XP
    • 15
      Filter by Substring (Case-Insensitive)
      Context. Filter a list of emails to those from a particular domain. Your task. Write emails_from_domain(df, domain) that returns rows where df["email"] contains domain as a substring (case-insensitive). Example. domain "gmail" matches "alice@gmail.com" and "carol@GMAIL.com" both. Notes. Case-insensitive match. No matches → empty DataFrame.
      7 min· 10 XP
    • 16
      Drop Rows with Any Missing Value
      Context. A CSV came in with some missing values — keep only complete rows. Your task. Write complete_rows(df) that returns rows where no column has a NaN. Example. A row with NaN in any single column is dropped, even if all other columns are filled. Notes. No NaNs anywhere → all rows kept. Every row has at least one NaN → empty DataFrame.
      7 min· 10 XP
    • 17
      Fill Missing Values in a Column
      Context. A survey has some blank age entries; fill them with a default so downstream math doesn't break. Your task. Write fill_missing_ages(df, default) that returns a new DataFrame where NaN values in the age column are replaced with default. Other columns are untouched. Example. age [30, NaN, 25, NaN], default=0 → [30, 0, 25, 0]. Notes. Only the age column is modified. df with no NaN in age → unchanged.
      7 min· 10 XP
    • 18
      Count Missing Values per Column
      Context. Audit a dataset — how many NaNs in each column? Your task. Write nan_counts(df) that returns a Series indexed by column name, with the count of NaN values in each column. Example. A df with 2 NaN in age and 1 NaN in city → {age: 2, city: 1, name: 0}. Notes. Include columns with zero NaN. Empty df → Series with one zero per column.
      7 min· 10 XP
    • 19
      Convert Column to Float
      Context. A CSV loaded every column as strings; you need price as floats so you can add them up. Your task. Write prices_as_float(df) that returns the price column as a Series of floats. Example. ["10.5", "20"] → [10.5, 20.0]. Notes. Use .astype() to convert dtype. Empty input → empty Series.
      7 min· 10 XP
    • 20
      Clean a Text Column with apply
      Context. A user table where names are messy (" alice ", "BOB"); clean each to a consistent format. Your task. Write clean_names(df) that returns the name column with each value stripped of surrounding whitespace and converted to title case. Example. " alice " → "Alice". "BOB" → "Bob". " JANE doe" → "Jane Doe". Notes. Use .apply() to run a function across the column. Empty df → empty Series.
      7 min· 10 XP
    • 21
      Replace Values with a Mapping
      Context. A survey used country codes ("US", "UK", "IN") but the final report needs full names. Your task. Write expand_country_codes(df, mapping) that returns the country column with each code replaced by its full name from the mapping dict. Example. codes ["US", "IN"], mapping {"US": "United States", "IN": "India"} → ["United States", "India"]. Notes. Codes not in the mapping are left unchanged. Empty mapping → all codes pass through unchanged.
      7 min· 10 XP
    • 22
      Sales per Region
      Context. A store manager has a table of orders, each tagged with a region. They want the total sales for each region. Your task. Write sales_per_region(df) that returns a pandas Series indexed by region, where each value is the sum of the amount column for that region. Example. Input df: | region | amount | | ------ | ------ | | North | 100 | | South | 50 | | North | 30 | Returns a Series: region North 130 South 50 Notes. - Region order in the result doesn't matter — we only compare keys → values. - Empty DataFrame → return an empty Series. - A region with only one order is still a valid group (its total is just that one value).
      7 min· 10 XP
    • 23
      Orders per Customer
      Context. An online store wants to know how many orders each customer placed. Your task. Write orders_per_customer(df) that returns a Series indexed by customer_id with the count of orders per customer. Example. Input rows (C1, O1), (C2, O2), (C1, O3) → Series {C1: 2, C2: 1}. Notes. Order of customers in the result doesn't matter. Empty DataFrame → empty Series.
      7 min· 10 XP
    • 24
      Average Rating per Genre
      Context. A reviews site wants the average rating for each movie genre. Your task. Write avg_rating_per_genre(df) that returns a Series indexed by genre, with the mean of the rating column for that genre. Example. Genre Action has ratings [4, 5] → entry Action: 4.5. Notes. Genre order in the result doesn't matter. A genre with one rating returns that rating as its mean. Empty DataFrame → empty Series.
      7 min· 10 XP
    • 25
      Sort by Column (Descending)
      Context. Show products in order of descending revenue for a top-line report. Your task. Write sort_by_revenue_desc(df) that returns the DataFrame sorted by revenue in descending order. Example. revenues [150, 50, 200, 100] → reordered as 200, 150, 100, 50. Notes. Stable sort — equal revenues keep their original relative order. Empty df → empty df.
      7 min· 10 XP
    • 26
      Top N by Column
      Context. Pull the top N best-selling products for a quick summary. Your task. Write top_n(df, n) that returns the n rows with the largest values in the sales column, ordered from highest to lowest. Example. 5 products, n=3 → top 3 by sales. Notes. n larger than the df → returns all rows in sorted order. n=0 → empty DataFrame.
      7 min· 10 XP
    • 27
      Sort by Multiple Columns
      Context. Order students by grade (A → F), then alphabetically by name within each grade. Your task. Write sort_grade_then_name(df) that returns the DataFrame sorted by grade ascending, then name ascending. Example. grades [B, A, A, B], names [Bob, Alice, Cher, Dan] → Alice/A, Cher/A, Bob/B, Dan/B. Notes. grade is the primary key; name is the tiebreaker. Empty df → empty df.
      7 min· 10 XP
    • 28
      Inner Join Two Tables
      Context. Combine an orders table with a customers table so each order row picks up the customer's name. Your task. Write attach_customer_name(orders, customers) that returns an inner-joined DataFrame on customer_id. Only customers present in both tables are kept. Example. orders has customer_ids 1, 2, 3; customers has 1, 2, 4 → result has only rows for IDs 1 and 2. Notes. Inner join. Result preserves the orders-table row order for matched rows. No overlap → empty DataFrame.
      7 min· 10 XP
    • 29
      Left Join with Fallback Value
      Context. Stack of orders, but the customer table doesn't have every customer. Keep all orders; fill missing names with "Unknown". Your task. Write attach_or_unknown(orders, customers) that left-joins on customer_id and replaces missing names with "Unknown". Example. orders for customers 10, 20, 30; customers has only 10, 20 → 3 rows, with name="Unknown" for the order with customer 30. Notes. Same row order as orders. Every order matched → no "Unknown" substitution.
      7 min· 10 XP
    • 30
      Stack DataFrames Vertically
      Context. Daily sales come in as separate DataFrames; combine them into a single table. Your task. Write combine(dfs) that takes a list of DataFrames and returns them concatenated vertically with the index reset to 0..N-1. Example. combine([df_mon, df_tue, df_wed]) → all rows stacked, re-indexed. Notes. Empty list → empty DataFrame (pd.concat([]) raises, so handle that). All dfs share the same columns.
      7 min· 10 XP
    Scenarios (0/3)
    CityBikes Ride Log
    You just joined CityBikes, a city bike-share network. Your manager drops last month's ride log on your desk — 24 rides, six columns. Before Monday's ops review she needs the basics: how big is the data, who rides longer trips, and what per-minute pricing would look like.
    with Devon·· 260 XP
    Skills you'll apply
    NumPy Arrays
    0/3
    Filtering
    0/3

    Module 3: Importing & Handling Files

    0/14 done
    Exercises (0/12)
    • 1
      Remove Duplicate Rows
      Context. A CSV export double-counted some records — the exact same row appears more than once. Your task. Write drop_duplicate_rows(df) that returns a DataFrame with duplicate rows removed, keeping the first occurrence of each. Example. If (Alice, NYC) appears twice, the result keeps just the first one. Notes. A row is a duplicate only if every column matches. Preserve the order of the rows that are kept. No duplicates → df unchanged.
      7 min· 10 XP
    • 2
      Tidy Column Headers
      Context. A CSV arrived with messy column headers — stray spaces, inconsistent casing, spaces inside names — making columns awkward to reference. Your task. Write tidy_headers(df) that returns the DataFrame with every column name cleaned: surrounding whitespace stripped, lowercased, and inner spaces replaced with underscores. Example. " First Name " → "first_name", "AGE" → "age". Notes. Only the headers change — the row data is untouched. Already-clean headers stay as they are.
      7 min· 10 XP
    • 3
      Fix a Numeric Column
      Context. A price column was read from CSV as text because the numbers contain thousands separators ("1,200"). You can't do math on it until it's numeric. Your task. Write fix_price_column(df) that returns the DataFrame with the price column converted to integers — strip the commas first, then change the type. Example. "1,200" → 1200, "980" → 980. Notes. Only the price column changes. Every value is a clean integer once the commas are gone.
      7 min· 10 XP
    • 4
      Normalize Text Values
      Context. An email column has inconsistent entries — stray spaces and mixed casing — so the same address looks different across rows. Your task. Write normalize_emails(df) that returns the email column as a Series, with surrounding whitespace stripped and every value lowercased. Example. " Alice@MAIL.com " → "alice@mail.com". Notes. Return a Series, not a DataFrame. Already-clean values pass through unchanged.
      7 min· 10 XP
    • 5
      Strip Separators from a Column
      Context. A phone column has numbers written with all kinds of separators — hyphens and spaces — and you need them as plain digit strings. Your task. Write clean_phone_numbers(df) that returns the phone column as a Series with every hyphen (-) and space removed. Example. "98-765 43210" → "9876543210". Notes. Return a Series. Remove both hyphens and spaces. Values with no separators pass through unchanged.
      7 min· 10 XP
    • 6
      Extract a Pattern
      Context. A label column mixes free text with a four-digit year somewhere inside each value. You want just the year. Your task. Write extract_year(df) that returns a Series with the first four-digit number pulled out of each label. Example. "Report 2021" → "2021", "Q3 2022 update" → "2022". Notes. Return a Series of strings. If a label has more than one number, take the first match. Every label contains at least one four-digit run.
      7 min· 10 XP
    • 7
      Parse a Date Column
      Context. A date column was read from CSV as plain text. Date math and .dt operations only work once it's a real datetime type. Your task. Write to_datetime_column(df) that returns the date column converted to a pandas datetime Series. Example. the text "2021-03-15" becomes a real datetime value. Notes. Return the converted Series. Every value is a valid YYYY-MM-DD date string.
      7 min· 10 XP
    • 8
      Extract the Month from Dates
      Context. You have a column of dates and want to group sales by month — first you need the month number out of each date. Your task. Write extract_months(df) that parses the date column and returns a Series of month numbers (1-12). Example. "2021-03-15" → 3, "2021-12-01" → 12. Notes. Return a Series of integers. Parse the text to datetime first, then reach for the month.
      7 min· 10 XP
    • 9
      Filter Rows by Year
      Context. An events table covers several years; you want only the events from one specific year. Your task. Write rows_in_year(df, year) that returns the rows of df whose date falls in the given year. Keep the date column exactly as it came in. Example. year=2021 keeps only rows dated in 2021, in their original order. Notes. Parse the dates to compare years, but don't change the returned date values. No matching rows → empty DataFrame.
      7 min· 10 XP
    • 10
      Summarize with a Pivot Table
      Context. A flat table of sales rows — each tagged with a region and a quarter — needs to become a region-by-quarter summary grid. Your task. Write sales_pivot(df) that returns a pivot table with one row per region, one column per quarter, and the sum of amount in each cell. Turn the region index back into a regular column. Example. rows for East/Q1 and East/Q2 collapse to a single East row with a Q1 and a Q2 column. Notes. Several rows can share the same region+quarter — sum them. Every region/quarter combination in the data has at least one row.
      7 min· 10 XP
    • 11
      Reshape Wide to Long
      Context. A scores table is wide — one column per subject. For analysis you need it long: one row per student-subject pair. Your task. Write to_long(df) that melts the wide table into columns student, subject, and score. Example. | student | math | science | |---------|------|---------| | Alice | 90 | 85 | becomes rows (Alice, math, 90) and (Alice, science, 85). Notes. student stays as an identifier column. Every other column becomes a subject value.
      7 min· 10 XP
    • 12
      Reshape Long to Wide
      Context. A scores table is long — one row per student-subject pair. For a report card you need it wide: one row per student, one column per subject. Your task. Write to_wide(df) that pivots the long table so each student is a row and each subject is its own column holding the score. Turn the student index back into a regular column. Example. rows (Alice, math, 90) and (Alice, science, 85) collapse to one Alice row with math and science columns. Notes. Each student-subject pair appears exactly once. Every student has a score for every subject in the data.
      7 min· 10 XP
    Scenarios (0/2)
    Wanderstay Booking Log Cleanup
    You just joined Wanderstay, a boutique hotel group with properties in Lisbon, Barcelona, Kyoto and Cape Town. The front-desk system's booking export is a mess — stray spaces in the headers, missing cities, a double export that duplicated rows, room types typed six different ways, and rates stored as text. Nadia in revenue ops won't touch it until it's clean.
    with Nadia·· 340 XP
    Skills you'll apply
    CSV Cleanup
    0/3
    String Cleanup (Series)

    Module 4: Data Visualization

    0/19 done
    Exercises (0/18)
    • 1
      Plot a Revenue Trend
      Context. Six months of revenue numbers are sitting in a list. A line chart makes the trend obvious at a glance. Your task. Write make_line_chart(months, revenue) that creates a figure, draws revenue over months as a line, and returns the axes. Example. fig, ax = plt.subplots() gives you a figure and axes; the line goes on the axes. Notes. Return ax, not fig. One line only — don't add extra plots.
      7 min· 10 XP
    • 2
      Bar Chart of Category Sales
      Context. Four product categories, four sales totals. Bars are the natural way to compare categories side by side. Your task. Write make_bar_chart(categories, sales) that draws one bar per category with its sales as the height, and returns the axes. Example. Toys gets a bar of height 500, Books one of height 320, and so on. Notes. Keep the categories in the order given. Return ax.
      7 min· 10 XP
    • 3
      Scatter Hours vs Scores
      Context. Eight students, their study hours and their test scores. A scatter plot shows whether the two move together. Your task. Write make_scatter(hours, scores) that draws one dot per (hours, scores) pair and returns the axes. Example. The student with 1 hour and score 52 becomes the dot at (1, 52). Notes. Use a scatter, not a line — dots live in ax.collections, not ax.lines.
      7 min· 10 XP
    • 4
      Title and Label a Chart
      Context. A chart without a title or axis labels is a guessing game. This week's temperatures need a chart someone else can read. Your task. Write make_labeled_chart(days, temps) that plots temperature over days as a line, sets a title mentioning "Temperature", labels both axes, and returns the axes. Example. Title "Daily Temperature", x-label "Day", y-label "Temperature (C)". Notes. The title just has to contain the word Temperature — the exact wording is up to you. Both axis labels must be non-empty.
      7 min· 10 XP
    • 5
      Add a Legend
      Context. Online and in-store sales share one chart. Without a legend nobody knows which line is which. Your task. Write make_legend_chart(months, online, store) that plots two lines — online labeled "Online", store labeled "Store" (in that order) — adds a legend, and returns the axes. Example. Pass label="Online" to the first plot call, then ax.legend() picks the labels up. Notes. Plot Online first, Store second — the legend order follows plotting order. Labels are case-sensitive.
      7 min· 10 XP
    • 6
      Style the Sales Line
      Context. The monthly sales line is going on a slide and the brand color is red. Styling is part of the job. Your task. Write make_styled_line(months, sales) that plots sales as a red line, sets a title mentioning "Sales", and returns the axes. Example. color="red" in the plot call; title like "Monthly Sales". Notes. Use the color name "red" exactly (not a hex code) — the checker reads back the color you set.
      7 min· 10 XP
    • 7
      A Two-Panel Figure
      Context. Revenue and orders belong side by side — one figure, two panels, so the reader compares without scrolling. Your task. Write make_grid(revenue, orders) that creates a 1×2 grid with plt.subplots(1, 2), plots revenue as a line in the left panel and orders as a line in the right panel, and returns both fig, axes. Example. fig, axes = plt.subplots(1, 2) — then axes[0] is the left panel, axes[1] the right. Notes. Return the pair: return fig, axes. One line per panel.
      7 min· 10 XP
    • 8
      Line and Bars Side by Side
      Context. A mini-dashboard: the monthly revenue trend on the left, a regional comparison on the right. Different chart types, one figure. Your task. Write make_dashboard(months, revenue, regions, region_sales) — a 1×2 grid with a revenue line in the left panel and one bar per region in the right panel. Return fig, axes. Example. axes[0].plot(...) for the trend, axes[1].bar(...) for the regions. Notes. Each panel has its own plotting methods — nothing is shared between them.
      7 min· 10 XP
    • 9
      Title Every Panel
      Context. Two untitled panels force the reader to guess. Each panel gets its own title. Your task. Write make_titled_grid(revenue, orders) — a 1×2 grid, revenue line left titled exactly "Revenue", orders line right titled exactly "Orders". Return fig, axes. Example. axes[0].set_title("Revenue") titles just the left panel. Notes. Panel titles are set on each axes, not on the figure. Exact spelling matters here.
      7 min· 10 XP
    • 10
      Histogram of Review Ratings
      Context. Twenty product reviews, rated 1-5. A histogram shows how the ratings spread — are customers happy or split? Your task. Write make_hist(df) that draws a seaborn histogram of the rating column with 5 bins, and returns the axes. Example. sns.histplot(...) with x="rating" and bins=5. Notes. Seaborn functions return the matplotlib axes they drew on — return that directly.
      7 min· 10 XP
    • 11
      Box Plot of Scores by Team
      Context. Two teams, five scores each. A box plot compares the two distributions in one look — and its center line is the median. Your task. Two parts: (1) write make_box(df) that draws a seaborn box plot with team on x and score on y, returning the axes; (2) set median_a to team A's median score, as a float. Example. The line inside team A's box sits exactly at median_a. Notes. For the median: filter the team A rows, take the score column, call .median(), wrap in float(...).
      7 min· 10 XP
    • 12
      Compare Two Groups in One Histogram
      Context. Daily step counts from users on two plans, Basic and Pro. Do Pro users actually move more? Overlay the two distributions to find out. Your task. Write make_grouped_hist(df) that draws a seaborn histogram of steps with 4 bins, split by plan using hue, and returns the axes. Example. hue="plan" colors each plan separately — and adds the legend for you. Notes. No manual legend() call needed; seaborn builds it from the hue values.
      7 min· 10 XP
    • 13
      Ad Spend vs Sales
      Context. Ten weeks of ad spend and sales. Before anyone claims "ads work", plot the relationship. Your task. Write make_scatter(df) that draws a seaborn scatter plot with ad_spend on x and sales on y, and returns the axes. Example. sns.scatterplot(...) — the column names become the axis labels automatically. Notes. Pass the DataFrame via data= and name the columns via x= / y=.
      7 min· 10 XP
    • 14
      Weekly Visitors Trend
      Context. Six weeks of site visitors. A line makes the direction of travel obvious — and a title makes the chart self-explanatory. Your task. Write make_trend(df) that draws a seaborn line plot of visitors over week, gives the chart a title (any wording), and returns the axes. Example. sns.lineplot(...) then ax.set_title("Weekly Visitors"). Notes. sns.lineplot returns the axes — keep a reference so you can title it.
      7 min· 10 XP
    • 15
      Color Points by Segment
      Context. Customer visits vs spend — but Basic and Premium customers behave differently. Coloring by segment turns one cloud of dots into two stories. Your task. Write make_segment_scatter(df) that draws a seaborn scatter of spend over visits, colored by segment using hue, and returns the axes. Example. hue="segment" — each segment gets its own color and a legend entry. Notes. The legend appears automatically once hue is set.
      7 min· 10 XP
    • 16
      Count Orders by Status
      Context. Ten orders, each with a status. How many were delivered, pending, cancelled? countplot counts and draws in one call. Your task. Write make_status_count(df) that draws a seaborn count plot of the status column and returns the axes. Example. sns.countplot(...) needs only x="status" — no counting on your side. Notes. Bars follow first-appearance order in the data: delivered, pending, cancelled.
      7 min· 10 XP
    • 17
      Average Score per Class
      Context. Three classes, two test scores each. The question is which class averages highest — and sns.barplot aggregates for you. Your task. Write make_class_averages(df) that draws a seaborn bar plot of score per class with errorbar=None, sets a title mentioning "Average", and returns the axes. Example. Class A's bar lands at 80 — the mean of 78 and 82. Notes. barplot shows the mean per category by default. errorbar=None hides the uncertainty whiskers.
      7 min· 10 XP
    • 18
      Quarterly Sales Heatmap
      Context. Sales by region and quarter — a 3×4 table of numbers. A heatmap turns the table into color, and annot=True keeps the numbers visible. Your task. Write make_heatmap(grid) that draws a seaborn heatmap of the grid with the values printed in each cell, sets a title, and returns the axes. Example. sns.heatmap(grid, annot=True) — the grid is already a table, so it goes in as-is. Notes. No melting or pivoting needed here; heatmap wants exactly this rows-by-columns shape.
      7 min· 10 XP
    Scenarios (0/1)
    The Conversion Drop
    You’re an analyst at Tempo. Trial-to-paid conversion is down 12% in four weeks. Marketing blames the pricing page, engineering blames the email flow, and the PM needs answers in two days.
    with Sarah·· 640 XP
    Skills you'll apply
    Dates & Times
    0/3
    Filtering
    0/3
    Functions
    0/3
    Loops
    0/3
    Sets & Tuples
    0/3
    Strings
    0/3
    CSV I/O (basic)
    0/3
    Selection & Indexing
    0/3
    Series & DataFrame
    0/3
    FitPulse Session Cleanup
    You're a product analyst at FitPulse, a fitness-tracker app. The workouts table just landed as a CSV export — and it's messy: blank values where sessions didn't sync, the same activity spelled four different ways. Clean it first, then find which activities users actually stick with.
    with Lena·· 410 XP
    Skills you'll apply
    Filtering
    0/3
    Group & Aggregate
    0/3
    CSV I/O (basic)
    0/3
    Missing Data
    0/3
    Sort & Rank
    0/3
    Transform Columns
    0/3
    Marketly Revenue Report
    Capstone
    You're a senior analyst at Marketly, an online marketplace. Leadership wants the monthly revenue report — but the data lives in three separate tables: orders, customers, products. Some orders have no status; some point at products that aren't in the catalog. Clean it, join it, and deliver the numbers.
    with Marcus·· 540 XP
    Skills you'll apply
    NumPy Arrays
    0/3
    Filtering
    0/3
    Group & Aggregate
    0/3
    CSV I/O (basic)
    0/3
    Merge & Concat
    0/3
    Missing Data
    0/3
    Sort & Rank
    0/3
    0/3
    Wanderstay Revenue Report
    Capstone
    The Wanderstay booking log is finally clean — you cleaned it. Now Nadia wants the quarterly report: when guests check in, how long they stay, and a month-by-room-type revenue table she can drop straight into the leadership deck.
    with Nadia·· 410 XP
    Skills you'll apply
    Dates & Times
    0/3
    Pivot & Reshape
    0/3
    Group & Aggregate
    0/3
    CSV I/O (basic)
    0/3
    Missing Data
    0/3
    Series & DataFrame
    0/3
    Sort & Rank
    0/3
    Matplotlib Basics
    0/3