Wrangling Data with pandas

Real data rarely arrives ready to analyze. This material shows how to get a pandas DataFrame into shape: find and handle missing values and duplicate rows, replace, map and bin values, clean text with string methods and regular expressions, and store repeated labels as categoricals. You then combine tables with merge, join and concat, and reshape them between long and wide layouts with hierarchical indexes, stack, unstack, pivot and melt. Every method comes with a small worked example and the mistakes to watch for, such as merges that silently add rows.

IntermediateLesson 4 of 8 in Data Analysis with Python Course

What you will learn

  1. Detect missing values and duplicate rows in a pandas DataFrame, decide whether to drop, fill or keep them, and check what each choice does to the data.
    • Detect missing values in a DataFrame
      Recognize how pandas marks missing data, count it per column and per row, and spot hidden codes that stand for missing values.
    • Drop or fill missing values
      Remove incomplete rows or columns with dropna, or fill gaps with fixed values, statistics, neighboring values or interpolation, and choose the method that fits the data.
    • Find and remove duplicate rows
      Mark and remove repeated rows with duplicated and drop_duplicates, compare only key columns when needed, and keep the right copy of each record.
  2. Change the values in a column in a controlled way: replace codes, map values through a lookup, convert types, rename labels, and turn numbers into groups, scaled values and indicator columns.
    • Replace, map and convert values
      Swap exact values with replace, translate values through a dictionary or function with map, rename rows and columns, and convert text to numbers safely.
    • Bin, scale, cap and encode values
      Group numbers into intervals with cut and qcut, rescale columns, limit extreme values with clip, and create indicator (dummy) columns for categories.
  3. Standardize and search text columns with the str accessor and regular expressions, and store columns with a fixed set of values efficiently as categoricals.
    • Clean text columns with string methods
      Use the vectorized str methods to trim, change case, split, search, replace and combine text in every row of a column at once.
    • Match and extract patterns with regular expressions
      Write simple regular expressions and use them with contains, extract, findall and replace to test, pull out and clean structured pieces of text.
    • Use the category dtype
      Convert columns with a limited set of values to categoricals, work with their categories and codes, and use ordered categories for values that have a natural order.
  4. Bring data from several tables together: match rows by key columns or index labels with merge and join, fill gaps from a second source, and stack tables with concat.
    • Merge DataFrames on key columns
      Combine two tables by matching values in key columns with merge, choose the join type, and check the result for unmatched and multiplied rows.
    • Join on the index and fill gaps from another table
      Merge on index labels with join or left_index and right_index, and use combine_first and label alignment to patch missing values from a second source.
    • Stack DataFrames with concat
      Glue tables together along rows or columns with pd.concat, control the resulting index and columns, and label the source of each part.
  5. Work with tables that have several index levels, and move data between long and wide layouts with stack, unstack, pivot and melt.
    • Work with hierarchical indexes
      Build and read a MultiIndex, select data by outer and inner levels, reorder and sort levels, and move levels between the index and the columns.
    • Reshape with stack and unstack
      Rotate column labels into the index with stack and index levels into columns with unstack, and handle the missing combinations that reshaping exposes.
    • Convert between long and wide format with pivot and melt
      Recognize long and wide layouts, turn long data into a wide table with pivot or pivot_table, and turn a wide table into long data with melt.

Wrangling Data with pandas study guide

5 chapters

1

Find and handle missing values and duplicates

A gap, a zero, and a repeated record have different meanings. Learn to measure missingness before choosing what to keep, remove, or estimate, and to identify duplicates using the columns that define a record. Small weather and order tables show how pandas makes these decisions and how they affect row counts, dtypes, and statistics. Each change has a visible result you can check before continuing with an analysis.

1.1 Find the gaps before calculating

Missing data means a value is unknown or unavailable. pandas stores a special marker in its place. Detect the marker before calculating totals or deciding which rows are usable.

Recognize the markers

The marker depends on the dtype. Float columns use NaN, datetime columns use NaT, and nullable types such as "Int64", "boolean", and "string" use pd.NA. pandas 3 infers text columns as str, with NaN for missing text. Python's None is also recognized as missing. Use import pandas as pd and import numpy as np for the examples.

df.isna() produces a Boolean table with the same rows and columns as df. A True marks a gap; df.notna() marks present values. The aliases isnull() and notnull() do the same jobs. Do not compare a column with np.nan: NaN is unequal even to itself, so s == np.nan misses the gaps.

Count cells and affected rows

Consider these weather readings:

citytemp_crain_mm
Lima18.00.0
OsloNaN4.0
Pune31.0NaN
KyivNaNNaN

A Boolean True counts as 1, so df.isna().sum() gives 0 missing cities, 2 temperatures, and 2 rainfall readings. df.isna().mean() gives shares of 0.0, 0.5, and 0.5. Each column's share uses all four rows as its denominator.

To find affected rows, use df.isna().any(axis=1). It returns False, True, True, True; summing gives 3. In contrast, df.isna().sum().sum() gives 4 missing cells. Kyiv contributes two cells but only one affected row. Use the measure that answers your question.

Recognize hidden missing codes

An instrument may record an unavailable reading as -999, "-", or "unknown". These are ordinary values until you convert them. After confirming the code's meaning, use df.replace([-999, "unknown"], np.nan). During CSV loading, na_values=[-999, "unknown"] adds these codes to the recognized markers. Empty CSV fields and common strings such as "NA", "n/a", and "NULL" are already recognized by default.

A zero rainfall reading can mean no rain, so replacing every zero would destroy real observations. Also, an empty string created in memory, "", and a string containing spaces are not missing to isna(). Inspect stored text as well as marker counts. To include missing categories in a frequency table, use value_counts(dropna=False).

Check calculations and dtypes

For s = pd.Series([4, np.nan, 8]), s.mean() is 6.0: it uses the two known values. s.count() is 2, while len(s) is 3. Dividing s.sum() by len(s) gives 4.0 and wrongly treats the gap as zero. Most statistics skip gaps by default; an all-missing column has sum 0 but mean NaN.

A gap can change storage too. pd.Series([3, None, 5]) becomes float64, holding 3.0, NaN, and 5.0. Giving dtype="Int64" keeps integer values with <NA> for the gap. The capital I identifies pandas' nullable integer dtype.

Key Takeaways

  • Use isna() to detect missing markers across dtypes.
  • Count missing cells separately from rows with at least one gap.
  • Convert confirmed missing codes; preserve valid zeros.
  • Check the number of known values used in a statistic.
  • Use "Int64" when integer values need a missing marker.

1.2 Choose what to drop or fill

Dropping a row removes its known values along with its gaps. Filling a gap keeps the row but introduces an estimate. Choose based on what the observation means and which columns the analysis needs.

Control which rows are removed

df.dropna() removes every row with any missing value. On the weather table, only Lima remains. Narrow the decision with subset, or require a minimum number of present cells with thresh.

CallRows keptReason
df.dropna(subset=["temp_c"])Lima, PuneTemperature is present
df.dropna(thresh=2)Lima, Oslo, PuneAt least two cells are present
df.dropna(how="all")All fourEvery row has a city

axis="columns" drops columns with gaps and keeps only city here. On a wide table, the default row rule may discard much of the data because a gap in any column is enough. Compare len(df) before and after removal.

Assign the result

Learn the reassignment form: df = df.dropna(). By default, cleaning methods return a new object. pandas 3.1 deprecates inplace=True for methods including dropna, drop_duplicates, and rename.

For one column, write df["temp_c"] = df["temp_c"].fillna(0). A chained call such as df["temp_c"].fillna(0, inplace=True) never updates the parent DataFrame under pandas 3 Copy-on-Write. Assignment makes the destination explicit.

Fill with a compatible value

A per-column dictionary lets a mixed table receive suitable values: df.fillna({"rain_mm": 0, "city": "Unknown"}). Columns omitted from the dictionary keep their gaps. A scalar such as 0 is convenient for an all-numeric table whose columns all accept it. Filling text with a number is incompatible; pandas 3.1 warns about this and a future version will reject it.

The known temperatures are 18.0 and 31.0. Their mean is 24.5, so df["temp_c"].fillna(df["temp_c"].mean()) puts 24.5 into both gaps. An extreme value can pull the mean strongly; a median is less sensitive. Before filling, keep an indicator with df["temp_missing"] = df["temp_c"].isna() so estimated and observed values remain distinguishable.

Mean filling reduces the apparent spread of the column. A distant fixed value can increase it. Filling rainfall with 0 asserts that no rain fell, which is inappropriate for a sensor that was off.

Use neighboring readings when order matters

ffill() carries the last known value forward; bfill() carries the next known value backward. For 12.0, NaN, NaN, NaN, 15.0, forward filling gives four 12.0 values followed by 15.0. ffill(limit=1) fills only the first gap in that run. A leading gap has no previous value and stays missing. Use ffill() directly; fillna(method="ffill") was removed in pandas 3.

interpolate() estimates between neighboring positions. For 10, NaN, NaN, 16, linear interpolation gives 10, 12, 14, 16. It fits smoothly changing measurements in a meaningful order. Carrying or interpolating across unrelated cities would impose a relationship the rows do not have.

After any operation, rerun isna().sum() as well as checking row counts. A successful method call tells you that the code ran; these checks tell you what changed.

Key Takeaways

  • Use subset, how, or thresh to express which gaps matter.
  • Assign returned results to the DataFrame or column you intend to change.
  • Choose fill values that fit both the column dtype and the meaning of the gap.
  • Preserve a missingness indicator before filling estimated values.
  • Use neighboring-value methods only when row order is meaningful.
  • Check row counts and missing counts after cleaning.

1.3 Identify the record before removing its copies

Two rows can look alike without describing the same event. Before removing duplicates, decide which values identify one record. An order ID may identify an order; customer and date alone may identify several legitimate purchases.

Mark the extra copies

df.duplicated() returns one Boolean per row. By default it compares every column, keeps the first occurrence, and marks later copies True. Summing the result counts extra copies, not all members of repeated groups.

Consider six order rows with IDs 101, 102, 102, 103, 104, 104. The two 102 rows are identical. The two 104 rows have different recorded weights, 0.5 kg and 0.8 kg. With every column compared, there is one extra copy: df.duplicated().sum() is 1. df.drop_duplicates() keeps five rows because the two different 104 rows are not exact copies.

Compare the columns that define identity

df.duplicated(subset=["order_id"]) compares IDs alone. It marks the second 102 and second 104, giving two extra copies. The other column values no longer affect this decision. Inspect the conflicting weights before choosing which order record to keep.

keep valueRepeated group behavior
"first"Keep the first row in the current order
"last"Keep the last row in the current order
FalseRemove every member of a repeated group

df.drop_duplicates(subset="order_id", keep="last") keeps the 0.8 kg version of order 104 in this order. Keeping the last row does not itself mean keeping the newest record. Establish that order first:

latest = df.sort_values("updated").drop_duplicates(
    subset="customer_id", keep="last"
)

Sorting by updated puts older records before newer ones. The duplicate check then compares only customer_id, so differing update times do not prevent two rows from being recognized as versions of the same customer.

Separate row values from index labels

These methods compare values, ignoring index labels. Identical rows labeled 4 and 9 still count as duplicates. Missing values at corresponding positions count as equal for this check. To test repeated labels themselves, use df.index.duplicated().

Exact comparison also preserves differences in case and whitespace. The values " Lisbon", "lisbon", and "LISBON " are distinct. Clean with .str.strip().str.lower() before checking duplicates when those spellings represent the same city. Otherwise nunique() will count three names for one place.

Check uniqueness where the relationship needs it

A lookup table should have one row per lookup key when each record must receive one set of details. lookup["customer_id"].is_unique checks that requirement. Repeated customer IDs in an orders table are normal because a customer can place several orders.

If a lookup key repeats, a later merge repeats each matching order for every matching lookup row. Removing duplicates from the orders would discard genuine purchases without fixing the lookup. Check the intended key on the correct table, inspect conflicting records, and compare the row count after removal.

Key Takeaways

  • Define a record with the correct key columns before removing copies.
  • duplicated().sum() counts later copies with the default keep="first".
  • Sort by update time before keeping the latest record.
  • Row-value duplicates and repeated index labels are separate checks.
  • Lookup-key uniqueness prevents accidental row multiplication in merges.
2

Transform values by replacing, mapping and binning

Transforming a column changes what its values mean or how they are represented. Choose between exact replacements, lookups, numeric conversion, intervals, scaled values, and indicator columns. You will work through the inputs and outputs of each method, including what happens to unmatched values and boundary values, so a convenient operation does not silently change categories or lose usable measurements.

2.1 Change values and labels deliberately

A replacement edits matching values, a lookup translates values, and a rename changes labels. These jobs can look similar in code but produce different results when a value is absent from a dictionary.

Replace exact values

s.replace("N/A", np.nan) changes only cells equal to "N/A". A list can name several old values: s.replace(["-", "?"], np.nan). A dictionary provides separate replacements, such as s.replace({"M": "male", "F": "female"}). Unmatched values remain as they were.

For a whole table, a nested dictionary restricts the edit to one column: df.replace({"status": {0: "open", 1: "closed"}}). This avoids changing unrelated zeros and ones elsewhere. Assign the returned result to keep the change.

replace matches entire cells. With "Main St" and "St", s.replace("St", "Street") changes only "St". To edit parts of a string, use s.str.replace("St", "Street", regex=False), which also produces "Main Street".

Translate with a lookup

For codes "PT", "JP", and "KE", compare the same dictionary in two methods:

MethodPTJPKE
s.map({"PT": "Portugal", "JP": "Japan"})PortugalJapanNaN
s.replace({"PT": "Portugal", "JP": "Japan"})PortugalJapanKE

A dictionary passed to map defines the available outputs. A missing key has no output and becomes missing. Check isna().sum() afterward so an incomplete lookup is visible.

map also accepts a function: s.map(lambda x: x * 1000) converts kilometer values to meters. For this arithmetic, s * 1000 is simpler and faster because it operates on the whole column. DataFrame.map applies a function cell by cell; it replaces the removed applymap method.

Convert text to numbers

astype requests a dtype, such as "float64", "Int64", or "category". It raises an error if a value cannot be converted. For text "12", "7.5", and "n/a", pd.to_numeric(s, errors="coerce") instead produces 12.0, 7.5, and NaN. Coercion preserves readable numbers but requires inspection of the failures.

Formatting can block conversion even when the number is valid. For "1,200" and "950", first remove the thousands separator:

cleaned = s.str.replace(",", "", regex=False)
numbers = pd.to_numeric(cleaned, errors="coerce")
failed = s.notna() & numbers.isna()

Here failed marks values that were present before conversion but became missing. Comparing missing counts alone gives a useful total; the mask also locates the original text to inspect. Remove known units from measured text in the same deliberate way before converting.

Rename the labels

df.rename(columns={"Qty": "quantity"}) changes a column heading, leaving its cells unchanged. df.rename(columns=str.lower) lowercases all headings. A sequence such as df.columns.str.strip().str.lower().str.replace(" ", "_", regex=False) turns "Total KG " into total_kg; assign it to df.columns.

Use replace or map for values inside a column. Use rename for the labels you select those values by.

Key Takeaways

  • replace preserves unmatched values; dictionary map makes them missing.
  • Use .str.replace to edit part of each text value.
  • Prefer vectorized arithmetic for simple column calculations.
  • Inspect newly missing values after numeric coercion.
  • rename changes labels and leaves data values unchanged.

2.2 Group and rescale measurements

Some analyses need exact measurements, while others need groups or comparable numeric ranges. Keep the original meaning clear: binning creates categories, scaling changes the numeric scale, and capping replaces extreme values.

Put values into intervals

pd.cut(ages, [18, 30, 45, 65]) uses the intervals (18, 30], (30, 45], and (45, 65]. The parenthesis excludes the left endpoint; the square closing symbol includes the right endpoint. Ages 19, 25, and 30 enter the first interval, 41 the second, and 64 the third. Age 18 becomes missing because it is on the excluded left edge.

include_lowest=True includes that lowest value in the first interval. right=False instead makes left-closed intervals such as [18, 30), so 30 enters the second interval. Values outside all intervals remain missing. Test the exact edges, as well as typical values between them.

There must be one label per interval, one fewer than the number of edges. With labels=["18-30", "31-45", "46-65"] and include_lowest=True, the six ages above give counts 4, 1, and 1. These labels describe whole-number ages; the interval boundaries still govern assignment.

Choose boundaries or group sizes
OperationWhat it controlsUseful purpose
pd.cut(x, [18, 30, 45, 65])Fixed boundariesMeaningful ranges
pd.cut(x, 4)Four equal-width intervalsCompare spans of values
pd.qcut(x, 4)Four groups with about equal countsCompare ranks in the data

For 2, 4, 6, 8, 10, 12, 14, 16, qcut gives two values per quartile group. Equal-count groups need not cover equal numeric widths.

Scale without creating groups

Min-max scaling uses (x - x.min()) / (x.max() - x.min()). For 10, 20, 40, the range is 30: the results are 0, about 0.333, and 1. Both the minimum and maximum are needed; dividing by the maximum alone is a different operation. A constant column has a zero range, so this expression cannot give it a useful scaled spread.

Standardizing uses (x - x.mean()) / x.std(). For 10, 20, 30, the mean is 20 and pandas' default sample standard deviation is 10. Subtracting 20 and dividing by 10 gives z-scores -1, 0, and 1. A z-score describes distance from the mean in standard-deviation units, not an interval label.

Cap and encode

pd.Series([5, 12, 250, 8]).clip(upper=100) gives 5, 12, 100, 8. Unlike a filter, clip keeps the row and replaces the extreme number. Inspect large values with describe() or a filter before capping: a genuine large measurement is still data.

pd.get_dummies(s) creates one Boolean indicator column per category. With red, green, red, blue, each row is True only in its color's column. Use dtype=int for zeros and ones. Missing values give all-false indicators unless dummy_na=True adds a missing-value column. For ordinary least-squares regression with an intercept, drop_first=True removes a redundant category column; other models may retain every category. With one category per row, the full set of indicators sums to the intercept's constant 1, creating linear dependence.

For cells containing several tags, s.str.get_dummies(sep="|") splits a value such as "Action|Comedy" into separate indicators. Binned intervals can also be passed to get_dummies, giving a column for each range.

Key Takeaways

  • Check interval endpoints and out-of-range values with cut.
  • cut controls ranges; qcut aims for similar group counts.
  • Min-max scaling and z-scores preserve numeric values on a changed scale.
  • clip caps values without removing rows.
  • Indicator columns represent category membership without inventing a numeric order.
  • Handle missing categories explicitly when their indicators matter.
3

Clean text and categorical data

Text values need consistent spelling and spacing before they can work as categories or keys. Apply string methods across a column, extract structured pieces with regular expressions, and choose a categorical dtype when a small set of labels repeats. The examples connect text cleaning to duplicate checks and merges, while showing how stored codes and category order differ from the words learners see in a table.

3.1 Make text values consistent

A person sees the same city in " Lisbon", "lisbon", and "LISBON ". pandas sees three different strings. Cleaning a text column gives later counts, duplicate checks, and merges a consistent set of values to compare.

Apply string methods across a Series

Python's "Lima ".strip() works on one string. For a column, use the .str accessor: s.str.strip() applies trimming to every non-missing value. s.lower() raises an AttributeError because the Series itself has no lower method. Use s.str.lower().

pandas 3 normally infers text as dtype str, replacing the older object default. The str dtype holds text and missing values, with NaN for missing text. String methods work on both types. Assign text to a str column; convert a number to text first if it belongs there as text.

Combine compatible cleaning operations and assign the result:

df["city"] = df["city"].str.strip().str.lower()

The three Lisbon spellings become "lisbon". strip() removes outer whitespace, not spaces inside a name. upper() makes all letters uppercase, while title() capitalizes each word, turning "new york" into "New York". Choose one consistent form for keys and counts.

Split structured strings

For "Silva, Ana" and "Tanaka, Yui", s.str.split(", ") returns lists such as ["Silva", "Ana"]. Add expand=True to produce separate columns. Retrieve a piece from each list with .str[0] or .str.get(1).

If only the first separator should divide a value, use n=1. Splitting "a,b,c" at commas with n=1 gives "a" and "b,c". Without a limit, it gives three pieces. Match the separator exactly, including any space that is part of the format.

Search and filter

s.str.contains("lis", case=False) checks for text anywhere while ignoring case. s.str.startswith("ID") checks the beginning; s.str.endswith(".csv") checks the end. The Boolean results can select rows:

mask = df["city"].str.contains("san", case=False, na=False)
selected = df[mask]

For the pandas 3 str dtype, missing text gives False in these search tests. Writing na=False also makes the intended handling clear when using nullable string data. contains reads a regex pattern by default; add regex=False for literal text that contains special characters.

Replace parts and build new strings

s.str.replace("kg", "", regex=False) removes "kg" wherever it appears. s.replace("kg", "") changes only cells that are exactly "kg". This distinction matters for values such as "12 kg".

str.replace treats its search text literally by default. For "3.5 kg", .str.replace(".", "", regex=False) removes the period and gives "35 kg". With regex=True, the period is a pattern for almost any character and removes all characters in this single-line example. Never use that change to parse a decimal measurement.

s.str.len() counts characters; a missing value produces NaN and can make the result a float column. s.str[:3] extracts the first three characters. To combine columns, df["first"] + " " + df["last"] joins Ana and Silva as "Ana Silva"; str.cat is another joining method. Each operation returns a result, so save it in the column you intend to use.

Key Takeaways

  • Use .str to apply text methods to a Series.
  • Trim and standardize case before matching text keys.
  • Control split output with expand and n.
  • Choose literal or regex searches explicitly for special characters.
  • Assign cleaned text back to retain the change.

3.2 Extract the pieces a text pattern describes

A regular expression, or regex, describes a pattern in text. It can recognize a quantity even when the number of digits or the spacing varies. Start with a small pattern whose parts you can explain.

Read the pattern pieces
PatternMeaning
\dOne digit
\wA word character: a letter, digit, or underscore
\sOne whitespace character
[A-Z]One uppercase letter in this range
+One or more of the preceding item
*Zero or more of the preceding item
?The preceding item is optional
{4}Exactly four repetitions
(…)A capture group

Write Python patterns as raw strings, such as r"\d+", so Python preserves their backslashes. A period matches any character except a newline by default. Escape it as r"\." to find an actual period. For a plain-text search, regex=False avoids pattern interpretation entirely.

Choose how much text must match

contains finds a pattern anywhere, match starts at the beginning, and fullmatch requires the entire value to fit. For "123a", s.str.contains(r"\d+") and s.str.match(r"\d+") succeed, but s.str.fullmatch(r"\d+") fails. Use the whole-string test when checking a complete code. case=False or flags=re.IGNORECASE after import re allows case-insensitive matching where supported.

Capture a measurement

Take "Rice 5 kg", "Flour 2kg", "Salt", and "Beans 12 kg, 3 kg". The pattern r"(\d+)\s*kg" means one or more digits captured as a group, followed by zero or more spaces and then "kg".

quantity = s.str.extract(r"(\d+)\s*kg", expand=False)
quantity = pd.to_numeric(quantity)

The extracted strings are "5", "2", a missing value, and "12". Conversion gives numeric values 5.0, 2.0, NaN, and 12.0. extract takes the first match in each row, so the second quantity in the beans description is not included. Without expand=False, a single group returns a one-column DataFrame.

Named groups label the output. s.str.extract(r"(?P<year>\d{4})-(?P<month>\d{2})") finds "2024-03" inside "Report 2024-03 final" and returns text columns year and month. The pattern recognizes the digit layout; numeric conversion is a separate step.

Find every match or clean by pattern

findall(r"\d+") gives a list per row, including ["12", "3"] for the beans text and [] for "Salt". count(r"\d+") counts matches. extractall gives one row per match with an extra index level named match.

With regex=True, s.str.replace(r"\s+", " ", regex=True) reduces runs of whitespace to one space. r"\D" means a non-digit, so removing it turns "+351 912-345-678" into "351912345678". The uppercase forms \S and \W similarly negate whitespace and word-character classes.

Repetition is greedy by default. In "<b>x</b>", r"<.*>" reaches from the first opening symbol to the last closing symbol. Removing that match removes everything. The lazy pattern r"<.*?>" matches each tag separately and leaves "x". Check sample outputs before using a pattern across a column.

Key Takeaways

  • Use raw strings to preserve regex backslashes.
  • Choose contains, match, or fullmatch for the intended scope.
  • Capture groups return text; convert extracted numbers separately.
  • extract takes the first match; findall and extractall retain multiple matches.
  • Escape special characters for literal regex matches.
  • Check greedy patterns against examples with several possible matches.

3.3 Store repeated labels as categories

A column with a few values repeated many times can use the category dtype. It stores the distinct labels once and represents each row with a small integer code. The visible value is still the label.

Understand categories and codes

For sizes M, S, L, M, M, s.astype("category") infers the categories L, M, S in alphabetical order. The codes are 1, 2, 0, 1, 1. Use s.cat.categories to inspect the labels and s.cat.codes to inspect the identifiers. A code of 0 means the first stored category; it is not a measured quantity.

Repeated labels can save considerable memory. With up to 126 categories, codes use one byte each; at 127 categories they use two bytes. In a pandas 3.1 setup with PyArrow, three country names repeated across 3,000,000 rows used about 3 MB as categorical data, about 42 MB as str, and about 165 MB as object. The text storage backend affects these figures. A column with almost all unique values gets no benefit from storing a category list plus a code per row.

Define meaningful order

Alphabetical size order puts L before M before S. Define the actual size order explicitly:

size_type = pd.CategoricalDtype(["S", "M", "L"], ordered=True)
sizes = s.astype(size_type)

Now sorting follows S, M, L, comparisons such as sizes > "S" are meaningful, and sizes.max() is "L" when L occurs. For unordered labels such as countries, equality tests work but greater-than comparisons raise a TypeError. as_ordered() uses the existing category sequence; inspect it before declaring that sequence meaningful.

Change the allowed labels

You cannot assign "XL" to a categorical that allows only S, M, and L. First assign the expanded result, sizes = sizes.cat.add_categories("XL"), then assign the new value.

Converting text to a fixed category list can turn unlisted values into missing values. pandas 3.1 warns that this lossy conversion will become an error. Check the source values with value_counts() before conversion, especially after cleaning spellings.

The .cat methods serve different purposes:

  • rename_categories({"S": "small"}) changes a label while keeping its members.
  • remove_categories("L") removes an allowed label and makes existing L values missing.
  • set_categories([...]) replaces the whole category list.
  • reorder_categories([...], ordered=True) changes the order without changing which labels are allowed.
  • remove_unused_categories() removes labels with no current members.

value_counts() displays every allowed category, including those with zero observations. With an S/M/L type containing only S and M, L still appears with count 0. This exposes an empty group that ordinary text data has no stored label for.

Keep categories useful downstream

cut produces categorical intervals, and get_dummies converts category membership into indicator columns. Giving unordered country codes to a model as numbers would impose an artificial order and distance. Indicators avoid that interpretation.

Concatenating categorical columns with different category lists normally loses the categorical dtype. Align their category lists first, or use pd.concat(..., union_categories=True) in pandas 3.1 when combining the lists is intended. Inspect both the resulting dtype and the category order.

Key Takeaways

  • Categoricals store a label list plus integer identifiers.
  • Declare category order explicitly when ranking has meaning.
  • Add a category before assigning a new label.
  • Removing a used category makes its values missing.
  • Use indicators for unordered categories in numeric models.
  • Align category lists or deliberately union them before concatenation.
4

Combine DataFrames

Combining tables can add information, add observations, or fill gaps from a backup. Those are different tasks. Match explicit keys with merge, work with index labels through join and combine_first, and stack compatible pieces with concat. Before trusting a result, predict which rows should survive and how many matches each key should produce; the examples show why unexpected growth often starts with a repeated lookup key.

4.1 Predict what a merge will keep

A merge matches rows through key values and places their information side by side. The key must describe the same thing in both tables, such as the customer ID on an order and the customer ID in a lookup table.

Name the key and choose the join

Use orders.merge(customers, on="customer_id"). Without on, pandas uses all column names shared by both tables. An unrelated shared column such as date could then prevent valid customer matches.

Suppose orders have customer IDs 10, 20, 20, 30, 50. The customer table has one row each for 10, 20, 30, 40.

howWhat survivesResult rows
"inner"Matching keys on both sides4
"left"Every left row and its matches5
"right"Every right row and its matches5
"outer"Keys from either side6
"cross"Every left/right row pair20

Inner is the default. For a left merge, customer 50 has missing customer details. A right merge includes customer 40 without an order, plus both orders for 20. An outer merge includes both unmatched keys. A cross merge needs no key because it deliberately pairs every row. pandas 3 also provides left_anti and right_anti for rows with no matching key on the other side.

Check how many matches a row can have

Two left rows for key 20 and three right rows for key 20 produce six pairs. The merge does not choose one right row. In the example, adding a second customer lookup row for 20 increases the inner result from four rows to six.

Express the intended relationship with validate:

merged = orders.merge(
    customers, on="customer_id", how="left",
    validate="many_to_one"
)

This allows many orders per customer but requires unique right-hand customer IDs. A repeated right key raises MergeError. one_to_one requires uniqueness on both sides; one_to_many requires it on the left. many_to_many allows repeats on both sides and does not check uniqueness.

Investigate unmatched rows

An outer merge with indicator=True adds _merge. Here its counts are four both, one left_only, and one right_only. These are rows, not distinct keys: repeated orders can produce several both rows for one customer.

To count distinct unmatched order-side keys, use m.loc[m["_merge"] == "left_only", "customer_id"].nunique(). Inspecting the rows can reveal missing lookup entries or inconsistent text.

Control names and key formats

If keys have different names, use left_on="customer_id", right_on="id". Both key columns remain available. Several keys can be matched together with on=["city", "year"]; both must agree.

Overlapping non-key names receive _x and _y. Choose meaningful suffixes such as suffixes=("_order", "_ship") to keep the origins clear.

Keys need compatible dtypes and equal values. Numeric 1 can match numeric 1.0, but integer and text key columns can raise a merge error. Convert their formats deliberately. "ana" and "Ana " differ as text; standardize case and outer spaces before merging when they identify the same person.

Missing keys also need inspection: pandas matches missing keys on one side with missing keys on the other. If both sides contain several such rows, those matches can multiply too.

Key Takeaways

  • Name merge keys explicitly.
  • Choose the join type by which source rows must survive.
  • Repeated keys produce every matching pair.
  • Use validate to check the intended key relationship.
  • Use an outer merge with an indicator to inspect unmatched rows.
  • Check key values, formats, and suffixes before using the result.

4.2 Combine values at matching index labels

An index can hold the key you want to match. pandas uses those labels when joining tables, patching missing values, or adding two Series. Row position alone does not decide the matches.

Join on index labels

left.join(right) matches the two indexes and defaults to a left join. In contrast, merge defaults to an inner join. You can also write left.merge(right, left_index=True, right_index=True) when both keys are in the index.

Suppose stations has index labels A, B, C and elevations 120, 850, 15 m. A readings table has station index labels A, B, B, D. readings.join(stations) keeps four rows and adds elevations 120, 850, 850, and NaN. The two B readings both receive the B elevation. With how="inner", only the three A/B readings survive.

If the readings keep station IDs in an ordinary column, use readings.join(stations, on="station"). The left column matches the right index. The equivalent merge form is readings.merge(stations, left_on="station", right_index=True, how="left").

Keep the key location explicit

set_index("station") moves the station column into the index. reset_index() restores it as an ordinary column. These let you choose a layout that makes the match easy to inspect. left.join([t1, t2]) can join several tables sharing index labels at once.

If both joined tables contain a non-key column such as source, join raises a ValueError unless you distinguish the names. In pandas 3.1, write suffixes=("_left", "_right"); this replaces the older lsuffix and rsuffix arguments. The suffixes keep both versions without deciding which value is authoritative.

Patch gaps from a backup

a.combine_first(b) keeps known values in a, fills its gaps from b at matching labels, and includes labels found only in b.

Labelaba.combine_first(b)
a1101
bNaN2020
c3303
dabsent4040

The result keeps 1 and 3 because the first source already knows them. It uses 20 to fill the gap and adds label d with 40. Reversing the call changes which source has priority. This is useful when a main data source should override a backup, even when their row orders differ.

Recognize alignment in arithmetic

Let x have a = 5 and b = 7; let y have b = 1 and c = 2. x + y gives b = 8, with missing results at a and c because each lacks one operand. It does not add the first value of x to the first value of y.

x.add(y, fill_value=0) supplies zero for the missing side, producing a = 5, b = 8, c = 2. Use that rule when an absent contribution should count as zero. Label alignment is the same organizing idea behind these operations, but their value rules differ: join adds columns, combine_first fills gaps, and arithmetic computes with matched values.

Key Takeaways

  • join defaults to a left index match; merge defaults to inner.
  • In join(on="station"), the left column matches the right index.
  • Use suffixes to distinguish overlapping non-key columns.
  • combine_first gives priority to known values in the caller.
  • Arithmetic aligns by label; fill_value controls missing contributions.

4.3 Stack compatible tables with concat

Monthly files usually contain more rows of the same kind of observation. Concatenation collects those pieces into one table. It aligns labels on the other axis; it does not search for matching customer or order key values.

Stack rows and align columns

pd.concat([jan, feb]) stacks rows because the default is axis=0. Suppose January has three trips with columns trip and km, while February has two trips with trip, km, and note. The result has five rows and three columns. January's missing note cells become NaN.

join="outer" is the default: it keeps the union of column names. join="inner" keeps only shared names, so the result has five rows and the two columns trip and km. It still includes all five observations; the join setting controls the other axis.

Column order in the parts does not control the match. Exact names do. If one piece uses km and the other uses Km, the result contains two partly empty columns. Rename the headings before concatenating.

Decide what the row labels should mean

The original indexes survive by default. With starting indexes 0, 1, 2 and 0, 1, the result repeats labels 0 and 1. result.loc[0] selects two rows. Use ignore_index=True when those numbers have no meaning and you want fresh labels 0 through 4.

When source identity matters, use an outer index level:

result = pd.concat(
    [jan, feb], keys=["jan", "feb"], names=["month", "row"]
)

Now result.loc["feb"] selects February's two rows. keys preserves origin within a MultiIndex. In pandas 3, combining keys with ignore_index=True raises ValueError; choose source labels or a new plain index for that operation.

Place columns beside each other

pd.concat([x, y], axis=1) aligns rows by index label. For x with a = 1, b = 2 and y with b = 3, c = 4, the result has rows a, b, c. The x column is missing at c, and y is missing at a. With join="inner", only b remains.

OperationAdded alongLabels aligned on
concat(..., axis=0)RowsColumn names
concat(..., axis=1)ColumnsRow indexes
merge(..., on="id")Matching row pairsKey-column values
Check storage and collect pieces efficiently

A column mixing numeric values and text may become dtype object. If ordinary NumPy int64 data gains a gap, it becomes float64; pandas nullable "Int64" can keep integers with <NA>. Align dtypes before combining when the column has a defined type.

DataFrame.append was removed in pandas 2.0. For many files, collect DataFrames in a list and call pd.concat once after the loop. Rebuilding a growing result in every iteration repeatedly copies accumulated data and becomes slow.

Check that a row concat's length equals the sum of input lengths. Then inspect column names, missing counts, and dtypes.

Key Takeaways

  • Row concatenation aligns column names and adds input row counts.
  • join chooses shared or all labels on the other axis.
  • Use ignore_index for disposable labels or keys for source identity.
  • Column concatenation aligns row indexes.
  • Collect many parts first and concatenate once.
5

Reshape data with hierarchical indexes, pivot and melt

The same measurements can be arranged as rows, columns, or levels of an index. Use a MultiIndex to name observations with several keys, then move those keys with stack and unstack or with pivot and melt. Track which labels identify a cell and whether they are unique. That makes reshaping predictable and lets you tell an absent combination from a measured zero or a pair of records that needs an explicit aggregation.

5.1 Select rows with more than one index level

A MultiIndex gives each row several labels. A sales row might be identified by both city and year, written as a tuple such as ("Osaka", 2024). Each label has a level, and the order of those levels matters for selection.

Build and name the index

df.set_index(["city", "year"]) moves those columns into the index. pd.MultiIndex.from_product([["Lima", "Osaka"], [2024, 2025]], names=["city", "year"]) builds every combination of the supplied labels. Concatenation with keys also adds a level.

For the examples, sales has these index pairs and values:

cityyearsales
Osaka2024120
Osaka2025135
Lima202480
Lima202595

After setting the two index levels, df.index.names is ["city", "year"]. rename_axis(["town", "yr"]) changes the level names without changing their values.

Select an outer label or a complete tuple

df.loc["Osaka"] selects both Osaka rows. The selected city level drops away, leaving 2024 and 2025 as the index. df.loc[("Lima", 2025), "sales"] selects one cell and returns 95.

A single label passed to loc searches the outermost level. df.loc[2024] therefore looks for a city named 2024 and raises KeyError in this table. To select a year across cities, use df.xs(2024, level="year"). It returns Osaka's 120 and Lima's 80, with city as the remaining index.

For a more general selection, df.loc[pd.IndexSlice[:, 2025], :] keeps all cities at year 2025. The colon in the first position means all labels in that level; the final colon selects all columns.

Sort before slicing ranges

Exact-label selection does not need a globally sorted index, but slicing a label range needs appropriate sorting. Use df.sort_index() before df.loc["Lima":"Osaka"]. An unsorted MultiIndex can raise UnsortedIndexError for such a slice.

sort_index() sorts all levels in their current order, so cities come first and years within each city. sort_index(level="year") sorts by the named level. swaplevel("city", "year") puts year in the outer position; follow it with sort_index() to group and order the new arrangement. Swapping the level positions alone does not sort the rows.

Move labels back to columns

reset_index() restores both levels as columns. reset_index(level="year") restores only year and leaves city as the index. These operations let you prepare a table for merging or saving without losing its identifying values.

Columns can also have several levels. A reshape with sales and staff values may produce ("sales", 2024) and ("staff", 2025) column labels. df["sales"] selects all columns under the top-level sales label.

For a total by city, df.groupby(level="city")["sales"].sum() gives Lima 175 and Osaka 255. Use grouping for this summary; the old sum(level="city") argument was removed in pandas 2.0.

Key Takeaways

  • A tuple identifies a complete MultiIndex label across levels.
  • Single-label loc selection begins at the outermost level.
  • Use xs or IndexSlice to select an inner level.
  • Sort the index before label-range slicing.
  • Swap levels to change their roles and reset them to restore columns.
  • Use groupby(level=...) for summaries by an index level.

5.2 Move labels between the index and columns

stack and unstack change where labels sit. Values keep their identifying combination, but the table can expose combinations that had no observation.

Stack column labels into the index

Suppose a wide DataFrame has cities Osaka and Lima as its index and years 2024 and 2025 as its columns. Osaka has values 120 and 135; Lima has 80 and 95. Name the axes city and year to keep their meaning visible.

long = wide.stack() moves the year labels into a new innermost index level. With these single-level columns, the result is a Series containing four values:

  • ("Osaka", 2024) gives 120.
  • ("Osaka", 2025) gives 135.
  • ("Lima", 2024) gives 80.
  • ("Lima", 2025) gives 95.

The city labels repeat because each city now has one row per year. Stacking has not summed the values or created additional measurements.

Unstack an index level into columns

long.unstack() moves the innermost index level, year, into columns and recovers the wide arrangement. long.unstack("city") moves city instead, producing years as rows and cities as columns. A numeric level position such as unstack(0) can select the same level, but a name makes the intention easier to read.

OperationLabel movement
stack()A column level becomes an inner index level
unstack()An index level becomes a column level

If the input has hierarchical columns, stacking one level may leave another level as columns. For columns such as ("sales", 2024) and ("staff", 2024), stack("year") returns a DataFrame with sales and staff columns. Stacking all column levels returns a Series.

Keep missing combinations visible

Now suppose the long Series has no Lima 2025 entry. Unstacking makes a rectangular city-by-year table, so it introduces NaN at that position. Ordinary integer values become floats when this missing cell requires a float missing marker.

unstack(fill_value=0) can fill a newly absent combination with zero. Use it only if absence means zero, such as no sales recorded in a complete sales-event dataset. A missing sensor observation is unknown and should stay missing.

pandas 3 stack() keeps missing cells. Stacking the incomplete 2-by-2 table therefore returns four entries, including NaN. Add .dropna() if you want only the three known values. Removing gaps changes which combinations remain represented, so check the result's length.

Resolve duplicate combinations first

Unstacking needs a single value for each complete index combination. Two entries labeled ("Lima", 2025) compete for the same output cell and raise ValueError: Index contains duplicate entries, cannot reshape.

Use long.index.duplicated() to inspect repeated complete labels, or check the corresponding key columns before setting the index. Decide whether the rows are accidental copies or separate observations that should be combined. Removing or aggregating them is a data decision; unstack does not choose a rule for you.

Key Takeaways

  • stack moves column labels inward; unstack moves index labels outward.
  • Choose the level explicitly when the default is not the one you need.
  • Absent combinations appear as missing cells when widened.
  • pandas 3 stacking retains missing values unless you drop them afterward.
  • Each complete index combination must be unique for unstacking.

5.3 Move between long and wide tables

In long format, identifying columns accompany one value column. In wide format, measurements are spread across columns, such as one column per year. Choose the layout that makes the next task easy: long tables suit filtering and grouping, while wide tables support side-by-side comparisons.

Pivot unique pairs into cells

A long table with columns city, year, and sales becomes wide with df.pivot(index="city", columns="year", values="sales"). City labels form rows, years form columns, and sales fill the cells. It is equivalent to df.set_index(["city", "year"])["sales"].unstack().

Osaka's 2024 and 2025 sales of 120 and 135 become two cells in one row. Lima's 80 and 95 form the second row. Swapping the index and columns arguments transposes this arrangement. Omitting values pivots all remaining data columns and can create hierarchical columns, such as a sales level and a staff level above the years.

Decide whether repeats are errors or observations

pivot rearranges values and cannot combine them. Two Lima 2025 rows would require two values in one output cell, so they raise ValueError. Count repeated pairs with df.duplicated(subset=["city", "year"]).sum().

If separate observations should be combined, use pivot_table with a chosen aggregation. For values 95 and 105 at Lima 2025, its default mean gives 100.0. With aggfunc="sum", the cell is 200. If each pair should have appeared once, keep pivot so it reveals a data error instead of silently summarizing it.

Melt measurement columns into rows

Suppose a CSV gives columns city, 2023, 2024, and 2025. Osaka's values are 110, 120, 135; Lima's are 70, 80, 95. Melt the year columns:

long = wide.melt(
    id_vars="city", var_name="year", value_name="sales"
)

id_vars stays in place and repeats. Each measurement-column label becomes a year value; each cell becomes a sales value. Two rows and three measurement columns produce six long rows, including one such as Osaka, "2023", 110. Missing measurement cells also become rows unless you drop them afterward.

Because these column labels came from CSV text, the resulting years are text. Use long["year"] = long["year"].astype(int) before doing arithmetic with them. Integer input column labels would already give integer year values.

Control what gets melted and restore the shape

value_vars=["2024", "2025"] limits melting to those columns. Forgetting id_vars can melt city names into the same value column as measurements. Always distinguish identifiers from the columns that contain values.

Without explicit names, the value column is called value. The variable column inherits wide.columns.name, or is called variable if that name is empty. Explicit var_name and value_name make the result easier to use.

Pivot the six rows back with index="city", columns="year", values="sales" to restore the three year columns. After pivoting, city is the index and the column axis is named year. reset_index() restores city as a column, and rename_axis(columns=None) removes the column-axis name. Verify both the shape and the labels before merging or saving the table.

Key Takeaways

  • Long format puts measurement labels in rows; wide format puts them in columns.
  • pivot needs unique row/column pairs and performs no aggregation.
  • pivot_table combines repeated pairs using the chosen aggregation.
  • melt repeats identifiers and creates one row per measurement cell.
  • Check column-label types when they become values.
  • Reset indexes and axis names when a flat output table is needed.

Wrangling Data with pandas examples

5 Walkthroughs

1

Clean readings without losing their meaning

16 steps
Measure missingness, choose a suitable fill or removal, and resolve repeated records using a small weather table.
  1. 1
    IntroductionDecide what each gap means

    A weather table has four cities and two measured columns: temperature in °C and rainfall in mm. Some readings are missing. You will locate those gaps, decide which rows are usable, and distinguish a repeated record from a genuinely different observation. The useful result is a table whose remaining values still mean what you think they mean. Use import pandas as pd and import numpy as np for the code.

  2. 2
    ExplanationRecognize the missing marker

    A gap can appear as NaN, NaT, or pd.NA, depending on its dtype. None is also recognized as missing.

    Use df.isna() to make a Boolean table with True at each gap. Comparing with np.nan misses those values because NaN is unequal even to itself. The detection method lets the same check work across numeric, text, and datetime columns.

  3. 3
    ExampleCount the weather gaps

    Lima has temperature 18 and rainfall 0. Oslo lacks temperature but has rainfall 4. Pune has temperature 31 and lacks rainfall. Kyiv lacks both readings. Every city name is known.

    df.isna().sum() gives city 0, temperature 2, and rainfall 2. df.isna().mean() gives missing shares of 0, 0.5, and 0.5 because each column has four entries.

  4. 4
    Key InsightDistinguish cells from rows

    Kyiv has two missing cells, while Oslo and Pune have one each. There are four missing cells but only three affected rows.

    df.isna().sum().sum() counts the cells. df.isna().any(axis=1).sum() first marks whether each row has any gap, then counts those rows. The row-wise any prevents Kyiv from being counted twice.

  5. 5
    ChallengeFind a hidden missing code

    A sensor column contains 0, 7, -999, and NaN. Its documentation says -999 means the sensor was off. How many values should remain known after cleaning?

    ---reveal---

    Two: 0 and 7. Replace the documented -999 code with a missing marker, then use isna() again. Zero is a genuine reading here. Treating every zero as missing would remove a known value along with the unavailable readings.

  6. 6
    ExampleKeep rows that have a temperature

    Suppose the analysis needs temperature but can tolerate unknown rainfall. Use clean = df.dropna(subset=["temp_c"]). This keeps Lima and Pune.

    The default dropna() would keep only Lima because it checks every column. thresh=2 would also retain Oslo, whose city and rainfall are known. Choose the rule that expresses the columns the analysis actually needs.

  7. 7
    ExampleMark estimates before filling

    For a rainfall analysis that keeps all cities, start from the original four rows. Before filling temperatures, save df["temp_missing"] = df["temp_c"].isna().

    The known temperatures 18 and 31 average to 24.5. Assign df["temp_c"] = df["temp_c"].fillna(df["temp_c"].mean()) to fill the two gaps. The saved indicator still identifies Oslo and Kyiv as estimated readings.

  8. 8
    TipChoose the estimate by its meaning

    A mean fill uses the known readings as a simple estimate, but it reduces apparent variation. The median is less sensitive to extreme readings. Filling with zero states something different: the missing quantity was zero.

    A switched-off rain sensor does not establish zero rainfall. Use per-column fills compatible with their dtypes, such as a dictionary with numeric values for numeric fields and text for text fields.

  9. 9
    ConnectionUse order only when it carries information

    Forward filling carries the previous known value into a gap; backward filling uses the next. With 12, NaN, NaN, 15, ffill(limit=1) gives 12, 12, NaN, 15.

    Interpolation instead estimates between known positions: 10, NaN, NaN, 16 becomes 10, 12, 14, 16. These methods fit meaningfully ordered readings, not a list of unrelated cities.

  10. 10
    ChallengeKeep usable rows without claiming zero rain

    Three records contain A/19/2, B/21/NaN, and C/NaN/5 for city/temperature/rainfall. You need temperature comparisons and cannot estimate missing rain. What rows and values should you retain?

    ---reveal---

    Keep A and B by checking the temperature column with dropna(subset=["temp_c"]). Leave B's rainfall missing. Dropping on every column would lose B's usable temperature; filling its rainfall with zero would add an unsupported measurement.

  11. 11
    ExampleCompare complete records

    An orders table has IDs 101, 102, 102, 103, 104, 104. The two 102 rows are identical. The two 104 rows differ in recorded weight: 0.5 kg and 0.8 kg.

    df.duplicated().sum() reports one extra full-row copy. drop_duplicates() leaves five rows because the differing 104 records are not exact copies. Index labels do not affect this comparison.

  12. 12
    ExplanationChoose a key and a retained version

    Comparing only order_id marks the second 102 and the second 104 as repeats, giving two extra key copies. The question is now which version represents each order.

    keep="last" chooses the last in the current row order. To make that mean newest, sort by an update field first. keep=False removes all members of repeated groups, so it would discard both 104 records.

  13. 13
    ChallengeKeep the newest record

    Item H has updates 1, 4, 2 and weights 3, 6, 5 kg in that input order. How do you retain its newest record, and what weight survives?

    ---reveal---

    Sort updated ascending, then use drop_duplicates(subset="item", keep="last"). The latest update is 4, so 6 kg survives. Keeping the last input row without sorting would select update 2 and incorrectly retain 5 kg.

  14. 14
    TipStandardize before checking identity

    Text keys " Lisbon", "lisbon", and "LISBON " do not match exactly. Trim and lowercase them when those spellings identify the same city. Then inspect repeated keys.

    The table's purpose decides whether repetition is valid: many orders may share a customer, but a lookup intended to give one detail row per customer needs unique keys. Removing valid orders cannot repair a repeated lookup key.

  15. 15
    ChallengeLink text cleaning to record choice

    Customer records have keys " emi " and "EMI", updates 2 and 5, and balances 10 and 14. The older balance is missing in a third unrelated record. You need the latest record per customer. What should happen before any balance filling?

    ---reveal---

    Standardize the keys, sort by update, and keep the last row per cleaned key. The two Emi spellings become one customer with balance 14. Resolve record identity first so obsolete versions do not influence a later fill statistic.

  16. 16
    RecapCheck the result against the decision

    After cleaning, compare the row count, missing counts, and key uniqueness with the result you intended. A missingness indicator keeps estimates visible; a sorted key-based duplicate check keeps the chosen record version visible.

    Save returned objects through direct assignment. Under pandas 3 Copy-on-Write, a chained inplace call on a selected column does not update its parent DataFrame.

2

Transform codes and measurements

15 steps
Choose exact replacements, lookups, conversion, bins, scaled values, and indicators by the output you need.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionChoose the meaning of the output
  2. 2
    ExampleTranslate or edit a value
  3. 3
    ExampleConvert the text after cleaning its format
  4. 4
    Key InsightKeep headings separate from cells
  5. 5
    ChallengeLocate the source of a new gap
  6. 6
    ExampleRead the ends of an interval
  7. 7
    ExplanationInclude the lowest edge deliberately
  8. 8
    ConnectionChoose widths or counts
  9. 9
    ChallengeTest the boundary rule
  10. 10
    ExampleSubtract the minimum before dividing
  11. 11
    ExampleMeasure distance from the mean
  12. 12
    TipCap without deleting an observation
  13. 13
    ExplanationRepresent category membership
  14. 14
    ChallengeExplain an all-false indicator row
  15. 15
    RecapVerify the representation you created
3

Turn inconsistent text into useful labels

17 steps
Clean names, extract quantities from descriptions, and preserve the meaning of category labels and order.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionGive text a consistent structure
  2. 2
    ExplanationApply a method to every string
  3. 3
    ExampleSplit only where the format requires
  4. 4
    TipKeep a plain-text search literal
  5. 5
    ChallengePrepare a text merge key
  6. 6
    ExplanationBuild a quantity pattern
  7. 7
    ExampleExtract the first quantity
  8. 8
    ConnectionChoose all matches when one is insufficient
  9. 9
    ChallengeReject a partial code match
  10. 10
    TipLimit what a pattern consumes
  11. 11
    ExampleSeparate the label list from its codes
  12. 12
    ExplanationGive sizes their intended order
  13. 13
    TipMake room before assigning a new label
  14. 14
    ChallengeKeep empty categories visible
  15. 15
    ConnectionCombine categories across batches
  16. 16
    ChallengeClean before defining the allowed list
  17. 17
    RecapInspect both text values and storage
4

Combine tables and account for every row

18 steps
Predict key matches, inspect unmatched records, patch labeled values, and choose when to concatenate observations.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionChoose how the tables relate
  2. 2
    ExampleName the shared key explicitly
  3. 3
    ExplanationPreserve the side you need
  4. 4
    Key InsightExpect every matching pair
  5. 5
    TipMake the relationship a checked rule
  6. 6
    ExampleCount unmatched keys separately
  7. 7
    ChallengeFind the cause of extra rows
  8. 8
    ExampleMatch a left column to a right index
  9. 9
    TipDistinguish overlapping columns
  10. 10
    ExampleLet the main source take priority
  11. 11
    ConnectionFollow labels when adding values
  12. 12
    ChallengePatch the correct observation
  13. 13
    ExampleAdd observations by matching column names
  14. 14
    ExplanationKeep a source label or make a fresh index
  15. 15
    Key InsightAlign rows when adding columns
  16. 16
    ChallengeDiagnose new distance gaps
  17. 17
    ChallengeCombine batches before adding lookup details
  18. 18
    RecapAccount for additions and matches
5

Reshape values while keeping their labels

18 steps
Build a MultiIndex, move levels with stack and unstack, and choose pivot or melt for long and wide layouts.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionTrack the identity of each measurement
  2. 2
    ExampleGive a row two labels
  3. 3
    ExplanationSelect the level you actually mean
  4. 4
    TipSort after changing level roles
  5. 5
    ChallengeSelect an inner level without changing the data
  6. 6
    ExampleMove year columns into the index
  7. 7
    ExplanationChoose the level that becomes columns
  8. 8
    Key InsightRecognize a newly visible gap
  9. 9
    ChallengeFind why widening fails
  10. 10
    ConnectionChoose long or wide for the next task
  11. 11
    ExamplePivot a unique pair into a cell
  12. 12
    ExplanationAggregate only when it is intended
  13. 13
    ExampleMelt measurements while keeping identifiers
  14. 14
    TipCheck what type a column label becomes
  15. 15
    ChallengeReconstruct a missing wide cell
  16. 16
    ConnectionRestore a table ready for column operations
  17. 17
    ChallengeChoose the repair for repeated measurements
  18. 18
    RecapCheck shape, uniqueness, and meaning

Wrangling Data with pandas practice questions

50 Practice Questions in 5 sets

Set 1: Read the data and predict simple changes10 Practice Questions

Question 1 of 10

A float Series contains 6, NaN, and 12. What is its default mean?
  1. 6.0
  2. 18.0
  3. 12.0
  4. 9.0
Show answerHide answer
Answer: D
The mean uses the two known values, so it adds 6 and 12 and divides by 2 to give 9.0. Dividing by all three positions gives 6.0, which treats the missing entry as zero even though no zero was observed. len counts positions; the default mean skips gaps.

Question 2 of 10

A DataFrame has missing cells at row A/column x, row A/column y, and row C/column x. Every other cell is known. How many rows does df.isna().any(axis=1).sum() count?
  1. 3
  2. 4
  3. 1
  4. 2
Show answerHide answer
Answer: D
Rows A and C each contain at least one gap, so two rows are counted. Row A contributes only once to this row-wise test despite having two missing cells. A total of three would count missing cells instead of the rows affected by them.

Question 3 of 10

A table has columns item, mass_kg, and note. Row U has a known mass and a missing note; row V has a missing mass and a known note. Both item names are known. What does df.dropna(subset=["mass_kg"]) retain?
  1. Neither row
  2. V only
  3. U only
  4. Both rows
Show answerHide answer
Answer: C
Only row V has a gap in the column being checked, so it is removed and U stays. A missing note does not matter to this subset rule. Removing both rows would apply the broader default check across all columns instead of the specified mass-only check.

Question 4 of 10

A Series contains "A", "B", and "C". What does s.map({"A": "active", "B": "blocked"}) produce?
  1. "active", "blocked", "blocked"
  2. "active", "blocked", NaN
  3. "active", "blocked", "C"
  4. "A", "B", NaN
Show answerHide answer
Answer: B
The two dictionary keys translate to their named outputs, and C becomes missing because the lookup has no C entry. Keeping the original C is the behavior of replace with this dictionary. A dictionary passed to map defines outputs for the whole mapping, not just edits to selected values.

Question 5 of 10

Values 0, 10, and 20 are passed to pd.cut(x, [0, 10, 20]) with defaults. Where does 10 go?
  1. It goes in (0, 10].
  2. It belongs to both intervals.
  3. It goes in (10, 20].
  4. It becomes missing.
Show answerHide answer
Answer: A
The default intervals include their right endpoint and exclude their left endpoint. Ten therefore belongs to the first interval, whose right edge is 10. Putting it into the second interval would use left-closed intervals, which requires right=False.

Question 6 of 10

A city Series contains " Accra", "ACCRA ", and "accra". After s.str.strip().str.lower(), how many distinct values and rows remain?
  1. 1 distinct value, 3 rows.
  2. 3 distinct values, 3 rows.
  3. 1 distinct value, 1 row.
  4. 2 distinct values, 3 rows.
Show answerHide answer
Answer: A
Trimming and lowercasing make every entry "accra", so there is one distinct value across the three original rows. String cleaning changes values without removing records. A one-row result would require an additional duplicate-removal operation, which has not been performed.

Question 7 of 10

A product code is "72x". How do str.contains(r"\d+") and str.fullmatch(r"\d+") classify it?
  1. Contains is true; fullmatch is false.
  2. Both return true.
  3. Contains is false; fullmatch is true.
  4. Both return false.
Show answerHide answer
Answer: A
The code contains a run of digits, so the anywhere search succeeds. The final x prevents the entire code from fitting a digits-only pattern, so fullmatch fails. Treating the numeric prefix as a full match would accept text outside the required format.

Question 8 of 10

Deliveries have depot keys P, P, Q, R. A depot table has keys P, P, Q, S. How many rows result from an outer merge on the key?
  1. 8
  2. 7
  3. 6
  4. 9
Show answerHide answer
Answer: B
The two P rows on each side produce four pairs, and Q contributes one matched row. R and S each add one unmatched row, for seven in total. Six would miss one of the unmatched sides or undercount the repeated-key pairs; an outer merge keeps both sides' unmatched keys.

Question 9 of 10

Two-row batch A has columns id, km. Three-row batch B has columns id, km, flag. What shape results from pd.concat([a, b], ignore_index=True)?
  1. 6 rows, 3 columns
  2. 3 rows, 3 columns
  3. 5 rows, 3 columns
  4. 5 rows, 2 columns
Show answerHide answer
Answer: C
Row concatenation adds the input lengths, giving five rows, and keeps the union of column names, giving three columns. The two A rows receive missing flag values. Two columns would require join="inner"; resetting the index does not discard an extra column.

Question 10 of 10

A wide table has 3 station rows and columns station, dry, and wet. What length results from melting the two measurement columns with id_vars="station"?
  1. 9 rows
  2. 6 rows
  3. 3 rows
  4. 2 rows
Show answerHide answer
Answer: B
Each of the three station rows contributes one dry and one wet measurement, producing six long rows. The station identifier is repeated, not melted. Nine would incorrectly treat the identifier as a third measurement column; only the two selected value columns contribute to the row multiplier.
Set 2: Choose methods for common wrangling tasks10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

An ordered Series is 5.0, NaN, NaN, 11.0. What does ffill(limit=1) produce?
  1. 5, NaN, 11, 11
  2. 5, 8, NaN, 11
  3. 5, 5, NaN, 11
  4. 5, 5, 5, 11
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

IDs in row order are K, L, L, M, M, N. What IDs remain after drop_duplicates(subset="id", keep=False)?
  1. K, N
  2. L, M
  3. K, L, N
  4. K, L, M, N
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

A Series contains "900", "1,600", and "bad". After literal removal of commas and pd.to_numeric(..., errors="coerce"), what values remain?
  1. 900, 1600, 0
  2. "900", "1600", "bad"
  3. 900, NaN, NaN
  4. 900, 1600, NaN
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

A nonconstant Series contains 5, 15, and 25. What is the min-max scaled value of 15?
  1. 0.0
  2. 0.6
  3. 0.5
  4. 0.4
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

A categorical column allows "low" and "high". What must happen before "medium" can be assigned to a row?
  1. Sort its existing category codes.
  2. Remove unused existing categories.
  3. Add "medium" to its categories.
  4. Set its current order to true.
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

A Series contains "Park Rd", "Rd", and "Road". What results from s.replace("Rd", "Road")?
  1. "Park Rd", "Road", "Road"
  2. "Park Rd", "Rd", "Road"
  3. "Park Road", "Road", "Road"
  4. "Park Road", "Rd", "Road"
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

A sensor table has keys S, S, T and must retain all observations. Its lookup has one row for S and one for T. What merge settings express this requirement?
  1. how="left", validate="one_to_many"
  2. how="outer", validate="one_to_one"
  3. how="inner", validate="one_to_one"
  4. how="left", validate="many_to_one"
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

Series a has p = 4, q = NaN; Series b has q = 9, r = 2. What is a.combine_first(b)?
  1. p = 4, q = 9
  2. p = 4, q = 9, r = 2
  3. p = 4, q = NaN
  4. p = NaN, q = 9, r = 2
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

A table indexed by site/month has C/6 readings 2 and 5, D/6 reading 4, and C/7 reading 9. An analyst swaps the levels, then tries .loc["C"] to obtain totals by site for month 6. What correction preserves the readings and gives the needed totals?
  1. Group all rows by site before selecting the report: C = 16, D = 4.
  2. Use xs("C", level="site"), then group by month: 6 = 7, 7 = 9.
  3. Drop repeated site/month pairs, then select month 6: C = 2, D = 4.
  4. Use xs(6, level="month"), then group by site: C = 7, D = 4.
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

A long Series has site/period readings A/1 = 2, A/1 = 4, A/2 = 5, B/1 = 3. The two A/1 readings should be averaged; an absent combination is unknown. You need all site/period combinations in a wide report and then a long export. What plan fits?
  1. Average A/1, unstack, then stack and drop gaps: 3 known entries.
  2. Average A/1, unstack, then stack: 4 entries, one missing.
  3. Keep the first A/1, unstack, then stack: 4 entries, one missing.
  4. Average A/1, unstack with zero fill, then stack: 4 known entries.
Show answerHide answer

Save this lesson to see this answer.

Set 3: Trace labels, keys, and changing shapes10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

A Series contains eight distinct readings from 1 through 8. How many readings go into each of four qcut groups?
  1. 1
  2. 8
  3. 2
  4. 4
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

Descriptions are "Oats 18kg, 4kg" and "Tea". What does s.str.extract(r"(\d+)\s*kg", expand=False) followed by numeric conversion return?
  1. 18 and NaN
  2. 18 and 0
  3. 22 and NaN
  4. 4 and NaN
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

A Series has values "", " ", and None, created directly in memory. How many values does s.isna().sum() count?
  1. 3
  2. 1
  3. 2
  4. 0
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

Rows for item Z have update values 2, 5, and 3, with masses 8, 11, and 9 kg respectively. What mass survives after sorting update ascending and keeping the last duplicate item key?
  1. 8 kg
  2. 28 kg
  3. 11 kg
  4. 9 kg
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

A text column has values "small", "large", "medium". It is converted in pandas 3.1 to a fixed categorical with categories ["small", "large"], then counted with dropna=False. What counts appear?
  1. small 1, large 2, missing 0
  2. small 1, large 1, missing 1
  3. small 1, large 1, missing 0
  4. small 1, large 1, medium 1
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

An outer merge joins left keys A, A, B, D to right keys A, A, A, C. What are the _merge row counts for both, left_only, and right_only?
  1. 6, 2, 1
  2. 6, 1, 1
  3. 3, 2, 1
  4. 2, 2, 1
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

Series x has a = 2, b = 6, and y has b = 4, c = 8. After x.add(y, fill_value=0), what is the sum of the resulting values?
  1. 24
  2. 12
  3. 20
  4. 10
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

An index has outer city and inner year levels. After swaplevel("city", "year").sort_index(), what does .loc[2027] select?
  1. Rows from 2027 onward.
  2. Row at position 2027.
  3. The city labeled 2027.
  4. All rows in year 2027.
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

Event counts are N/1 = 7, N/2 = 9, S/1 = 3, with no S/2 record. The event log is complete, so absence means no events. A report must show every site/period pair and retain that coverage in a long export. What plan and result fit?
  1. Unstack, stack, and forward fill: 4 entries totaling 22.
  2. Unstack, stack, and drop gaps: 3 entries totaling 19.
  3. Unstack with zero fill, then stack: 3 entries totaling 19.
  4. Unstack with zero fill, then stack: 4 entries totaling 19.
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

A long table has P/2026 values 8 and 12, and Q/2026 value 6. What results from a pivot_table using site as rows, year as columns, and default aggregation, then melting the one year column?
  1. 2 rows with values 10, 6
  2. 3 rows with values 10, 10, 6
  3. 3 rows with values 8, 12, 6
  4. 2 rows with values 20, 6
Show answerHide answer

Save this lesson to see this answer.

Set 4: Diagnose changes in a cleaning pipeline10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

Readings in a numeric column are 2, 4, 30, NaN. You save a missingness indicator, then fill the gap with the column median. What value is filled and how many indicator values are true?
  1. 3 and 1
  2. 4 and 0
  3. 12 and 1
  4. 4 and 1
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

A table contains keys " seoul", "SEOUL", and "busan". Their data otherwise match within city. After trimming and lowercasing the key, how many rows remain when every member of repeated-key groups is removed?
  1. 3
  2. 1
  3. 2
  4. 0
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

Original numeric text is "5", "none", None, " 9 ". After to_numeric(errors="coerce"), how many originally present values have become missing?
  1. 3
  2. 0
  3. 2
  4. 1
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

A mass column contains 0, 10, 20, and one unknown reading. After cut with edges [0, 10, 20] and default indicators, two rows are all-false. You need valid masses categorized and unknown readings separately identifiable. What repair fits?
  1. Drop all-false rows and then retain the original interval edges.
  2. Change to right=False and leave missing indicators disabled.
  3. Include the lowest edge and use dummy_na=True when encoding.
  4. Fill unknown mass with 0, then cut with include_lowest=True.
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

What does s.str.split("/", n=1, expand=True) do to "zone/aisle/bin"?
  1. Creates 3 columns: "zone", "aisle", "bin".
  2. Creates 1 column holding the original text.
  3. Creates 2 columns: "zone", "aisle/bin".
  4. Creates 2 columns: "zone/aisle", "bin".
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

Codes are "T.8", "T18", and "T.12". You must select codes containing a literal period, then extract their final digits with r"(\d+)" and convert them. What numeric values remain?
  1. 8 and 12
  2. 8, 18, 12
  3. 18 only
  4. 8 and 1
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

Shipments have supplier keys " ax ", "AX", "by". A lookup has "ax", "ax", "by", with differing details on its two ax rows. What must be resolved before a left merge can reliably give one detail row per shipment?
  1. Only keep the first shipment for each key.
  2. Only standardize the shipment key text.
  3. Only rename the overlapping detail columns.
  4. Standardize keys and resolve duplicate lookup keys.
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

Table a has two rows with columns id, km. Table b has one row with columns id, Km. After row concatenation with default column handling, how many cells are missing in the two distance columns combined?
  1. 3
  2. 2
  3. 1
  4. 0
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

A region/year MultiIndex holds E/2025 = 3, E/2026 = 5, W/2025 = 7, W/2026 = 11. A report needs only 2026, with regions as columns. An analyst selects .loc[2026] and then pivots. What correction selects the data and produces a single report row?
  1. Select xs(2025, level="year"), restore year as 2026, then pivot year/region: 3 and 7.
  2. Select xs(2026, level="year"), restore year as 2026, then pivot year/region: 5 and 11.
  3. Select xs(2026, level="year"), restore year as 2026, then pivot year/region: 8 and 18.
  4. Sum by region, restore year as 2026, then pivot year/region: 8 and 18.
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

A table has three unique sites, with columns site, day, and night. One night cell is missing. After melting both measurements, dropping missing values, and pivoting back, what are the long row count and wide missing-cell count?
  1. 5 rows and 0 gaps
  2. 5 rows and 1 gap
  3. 6 rows and 0 gaps
  4. 6 rows and 1 gap
Show answerHide answer

Save this lesson to see this answer.

Set 5: Reason through linked transformations10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

An in-memory numeric column contains 0, -99, NaN, and 6. Its documentation defines -99 as missing and 0 as a real reading. After cleaning, what are the known count and mean?
  1. 2 known, mean 3
  2. 3 known, mean 2
  3. 3 known, mean -31
  4. 2 known, mean 6
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

Rows are A/2/unknown, B/unknown/8, C/unknown/unknown, D/6/10 for site/x/y. Keep every row with at least one known measurement, then fill only x from its surviving mean. Which row rule and final x sum fit?
  1. Keep at least 2 known cells per row; x sum 8.
  2. Drop every row with a missing measurement; x sum 6.
  3. Keep at least 2 known cells per row; x sum 12.
  4. Keep at least 1 known cell per row; x sum 16.
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

A Series contains codes "a", "b", and "z". The pipeline maps {"a": "4", "b": "bad"} and then applies numeric coercion. Which original entries finish missing, and why?
  1. Only b, because bad is not numeric.
  2. b and z, from conversion and lookup failure.
  3. Only z, because its key is absent.
  4. a and b, from mapping numeric-looking text.
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

Original measurements are 3, 9, 21, 45. A transformed column is 0, about 0.333, 1, 1 in the same order. Which operation sequence explains the output while keeping every row?
  1. Cap at 21, then min-max scale the capped column.
  2. Divide by 45, then cap scaled values at 1.
  3. Cap at 21, then divide the capped column by 21.
  4. Min-max scale the original column, then cap at 1.
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

An unordered categorical contains country labels and has integer codes. A model interprets numeric inputs as quantities. Why should these codes be replaced with category indicators?
  1. Codes cannot be stored in numeric columns.
  2. Indicators create a correct ranking of countries.
  3. Codes would impose invented order and distances.
  4. Codes use more memory than all text values.
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

Shipments have keys U, U, V, W. A supplier lookup has U/update 1, U/update 4, V/update 2, X/update 2. You need the latest details per supplier, every shipment retained, and the count of distinct unmatched keys across both sources. What plan fits?
  1. Keep latest lookup keys; audit with outer merge; inner merge has 3 rows and 2 unmatched keys.
  2. Keep latest lookup keys; audit with inner merge; left merge has 4 rows and 0 unmatched keys.
  3. Keep one shipment per key; audit with outer merge; left merge has 3 rows and 2 unmatched keys.
  4. Keep latest lookup keys; audit with outer merge; left merge has 4 rows and 2 unmatched keys.
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

Primary values are a = 5, b = NaN; backup values are a = 50, b = 7, c = 9. After primary.combine_first(backup), what would reversing source priority change?
  1. Only c disappears from the result.
  2. Only a changes from 5 to 50.
  3. Every result value changes.
  4. Only b changes from 7 to missing.
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

Two DataFrames each have columns kg and count, both using nullable "Int64". A third has only kg with that dtype. After row concatenation, what best explains the resulting count column?
  1. It loses every row from the third input.
  2. It stays nullable integer with gaps for the third input.
  3. It is text because an input omitted count.
  4. It becomes ordinary integer with zeros for the third input.
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

Event records are A/1 = 4, A/1 = 6, A/2 = 8, B/1 = 3. The log is complete, and a report must show each site/period total including pairs with no events, then export the same coverage as long rows. What plan gives the required export?
  1. Sum repeated pairs, unstack with zero fill, then stack: 4 rows, sum 21.
  2. Keep the first repeated pair, unstack with zero fill, then stack: 4 rows, sum 15.
  3. Average repeated pairs, unstack with zero fill, then stack: 4 rows, sum 16.
  4. Sum repeated pairs, unstack, then stack and drop gaps: 3 rows, sum 21.
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

Known event counts are A/1 = 4, A/1 = 8, A/2 = 6, B/1 = 5; B/2 has no record and is unknown. An analyst uses a default pivot table, then melts it, claiming to export totals for known pairs. Which revised output and reasoning fit?
  1. Four rows totaling 23, because melting recovers both A/1 records.
  2. Four rows totaling 23, because zero can replace the unknown pair.
  3. Three rows totaling 17, because the default mean is a pair total.
  4. Three rows totaling 23, after sum aggregation and removing unknown cells.
Show answerHide answer

Save this lesson to see this answer.

Stuck on your own question? Homework Help

Key Terms

72 Key Terms, each with its definition

A

Axis
An axis is a direction of a pandas object; concatenation along axis 0 adds rows and along axis 1 adds columns.
Choose axis 1 when placing two measurement Series beside each other at matching index labels.

B

Backward fill
A backward fill copies the next known value into earlier gaps, so its meaning depends on the ordering of the observations.
A backward fill takes the following known reading as the fill value for an earlier unavailable reading.
Bin
A bin is a numeric interval used to group measurements, with defined endpoints that determine where boundary values belong.
With a bin written (0, 10], the measurement 10 is included and the measurement 0 is excluded.

C

Capping
Capping replaces measurements beyond a chosen limit with the limit itself, keeping the observations while changing their values.
Capping at 80 kg changes a 95 kg entry to 80 kg, so inspect whether that extreme reading is genuine first.
Capture group
A capture group marks part of a regex with parentheses so the matched text can be returned as an extracted output field.
A capture group around the digits in a mass pattern returns "14" from the text "Lentils 14 kg".
Case normalization
Case normalization gives text a consistent letter case so upper- and lowercase versions can be compared as the same label.
Case normalization makes "PUNE" and "pune" match before their records are counted or merged.
Categorical dtype
A categorical dtype stores a defined list of labels and an integer code per row, with an optional meaningful ordering of the labels.
A categorical dtype can store a small set of repeated product-size labels more efficiently than repeated text.
Category code
A category code is an integer identifier pointing to a label in a categorical list, without implying a measured distance between labels.
A category code for a country is a storage identifier, so do not treat it as the country's numeric rank.
Category union
A category union combines the labels allowed by categorical inputs so concatenation can retain a categorical output across different lists.
Use a category union when two batches have different allowed colors and the combined column should keep both lists.
Character class
A character class describes one allowed character, using a shorthand such as \d or a range such as [A-Z] in a pattern.
The character class [A-Z] matches one uppercase letter in an inventory code before its digits.
Coercion
Coercion in to_numeric converts readable values to numbers and turns unreadable entries into missing values instead of raising.
After coercion, inspect the original text of every newly missing mass to find unrecognized formatting.
combine_first
The combine_first method keeps a first object's known values, fills its gaps from matching labels in another object, and adds new labels.
Use combine_first to patch missing measurements from a backup while keeping known values from the main source.
Concatenation
Concatenation places pandas objects along rows or columns while aligning labels on the other axis, without matching ordinary key values.
Row concatenation collects successive batches of parcel observations into one longer table.
Copy-on-Write
Copy-on-Write keeps changes to a selected pandas object from silently changing another object that shares its underlying data.
Under Copy-on-Write, assign a filled Series back to its DataFrame column to update the parent table.
Cross-section
A cross-section selects one value from a named index level across other levels, allowing direct selection of an inner level.
A cross-section for year 2026 retrieves that year across all cities in a city/year MultiIndex.

D

dropna
The dropna method removes rows or columns according to their missing values, with options for selected columns and required present counts.
Use dropna(subset=["mass_kg"]) when usable rows must have a mass even if another field is incomplete.
Duplicate row
A duplicate row repeats another row across all compared columns; by default pandas marks later copies and ignores index labels.
A duplicate row with a different numeric index still counts as a copy when all its data values match.

F

Forward fill
A forward fill copies the most recent known value into later gaps in meaningful row order, stopping at an optional run limit.
A forward fill can carry a stable instrument setting to the next row, but cannot fill a leading gap.
Full match
A full match requires the entire input string to fit a pattern, so a valid fragment inside a longer value is insufficient.
A full match of r"\d+" accepts "204" and rejects "204x", even though both strings contain digits.

G

Greedy repetition
Greedy repetition matches as much text as possible while allowing the remaining pattern to match, sometimes spanning several delimiters.
Greedy repetition in r"<.*>" can cover both tags and the text between them in a short marked-up string.

I

id_vars
The id_vars argument identifies columns to preserve during melting, separating observation identifiers from columns containing measurements.
Setting id_vars="station" keeps station names out of the numeric readings created by a melt.
ignore_index
The ignore_index option replaces labels along the concatenation axis with consecutive integers, preserving alignment on the other axis.
Set ignore_index=True when monthly files have disposable row numbers that would otherwise repeat.
Imputation
Imputation replaces missing data with estimated or chosen values, preserving rows while changing the observed distribution of a column.
Mean imputation fills a missing temperature with the average of the known readings and reduces apparent spread.
Index join
An index join matches tables through index labels; pandas join uses the right index even when a left key column is specified.
An index join can add station elevations to repeated readings without pairing them by row position.
Index level
An index level is one labeled component of a hierarchical index, such as city or year, with a position and an optional name.
Moving year to the outer index level makes year the first label used by a single-label loc selection.
Index sorting
Index sorting orders rows by their index labels, making label ranges usable and grouping a MultiIndex according to its level order.
Apply index sorting after swapping city and year levels so the rows follow the new year-first order.
Indicator column
An indicator column marks whether a row belongs to a category, usually with Boolean values or zeros and ones for numeric input.
An indicator column for "blue" is true only on rows labeled blue when created by get_dummies.
Inner join
An inner join keeps row pairs whose keys match on both sides, excluding observations that have no key match in the other table.
An inner join drops a delivery whose station ID has no matching row in the station lookup.
Interpolation
Interpolation estimates values between known readings; pandas linear interpolation uses their positions to fill a straight-line path.
Interpolation turns ordered readings 8, NaN, 12 into 8, 10, 12 when a straight-line estimate fits.

L

Label alignment
Label alignment pairs pandas values by their row or column labels, so differently ordered objects can still match the same observations.
When two Series are added, label alignment adds their b-labeled values together even when b occupies a different position in each Series.
Left join
A left join preserves each left-side row and its matches, supplying missing right-side fields when a left key has no match.
A left join keeps an order with an unknown customer key so its missing lookup details remain visible.
Literal matching
Literal matching searches for the exact characters supplied, giving special regex symbols such as a period no pattern meaning.
Use literal matching to remove the period from a code without treating it as a pattern for any character.
Long format
Long format stores measurement labels as values in identifying columns, with observations in rows and a shared measurement-value column.
Long format puts city, year, and sales into columns so each city/year observation occupies a row.

M

Many-to-one relationship
A many-to-one relationship allows repeated left keys but requires unique right keys, such as many orders matching one customer record.
Validate a many-to-one relationship when enriching deliveries from a one-row-per-station lookup.
map
The map method translates Series values through a function or lookup; a dictionary lookup makes unknown keys missing.
A country-code map exposes codes absent from the dictionary as gaps that the analyst can inspect.
melt
The melt method turns measurement columns into label/value rows while preserving and repeating the columns identified as row identifiers.
A melt of four year columns creates four rows per city, including rows for empty measurement cells.
Merge
A merge matches rows through key values and combines their fields, producing all matching pairs permitted by the chosen join type.
A merge attaches one customer lookup row to each order whose customer key has a matching value.
Merge indicator
A merge indicator labels each result row by match origin: both inputs, the left input alone, or the right input alone.
Count the merge indicator after an outer merge to locate rows that failed to match on either side.
Min-max scaling
Min-max scaling subtracts a column minimum and divides by its nonzero range, mapping its observed minimum to 0 and maximum to 1.
For readings 2, 5, 8, min-max scaling gives 0, 0.5, 1 using the original range of 6.
Missing combination
A missing combination is a set of labels with no corresponding observation, which widening can expose as a missing cell in a rectangle.
A missing combination for a station and month appears as NaN when the long readings are unstacked.
Missing value
A missing value marks an observation that is unknown or unavailable, using a marker recognized by pandas instead of ordinary data.
A missing value in rain_mm means the reading is unavailable, so it must be distinguished from no rain.
Missingness mask
A missingness mask is a Boolean object marking where values are missing, with the same labels and shape as the object being checked.
Summing a missingness mask by column shows how many temperature and rainfall cells need attention.
MultiIndex
A MultiIndex identifies a pandas row or column with several label levels, so a complete label is a tuple of values across those levels.
A MultiIndex with city and year identifies the same city's measurements separately for each year.

N

NaN
NaN is a missing marker used in float columns and pandas 3 default text columns; it is unequal to itself in ordinary equality tests.
Use isna() to find NaN in the temperature column before counting the readings available for a mean.
NaT
NaT is the pandas missing marker for date and time values, allowing a datetime column to represent an unavailable date or timestamp.
An absent inspection date appears as NaT, and isna() detects it alongside missing numeric readings.
Nullable dtype
A nullable dtype stores values of a specified type together with a missing marker, such as "Int64" for integers with pd.NA.
A nullable dtype keeps parcel counts as integers even when one record has an unknown count.

O

Ordered categorical
An ordered categorical has a declared label sequence that governs sorting and ordered comparisons instead of alphabetical spelling.
An ordered categorical with S, M, L puts sizes in their intended sequence when the column is sorted.
Outer join
An outer join preserves keys found on either side, showing missing fields where a key lacks a matching row in the other input.
An outer join exposes both orders without customer details and customers without any recorded order.

P

pivot
The pivot method widens long data by placing chosen index/column pairs into cells, raising an error when a pair identifies several values.
A pivot of station and month requires one reading for each station/month cell in the output.
pivot_table
The pivot_table method combines values for repeated index/column pairs through an aggregation, using the mean when none is specified.
Use pivot_table with aggfunc="sum" when separate shipments should add into a daily total.

Q

Quantile binning
Quantile binning divides ranked measurements into groups with about equal observation counts, allowing their numeric widths to differ.
Use quantile binning with qcut to compare low, middle, and high groups containing similar numbers of readings.

R

Raw string
A raw string is a Python string form prefixed with r that preserves backslashes for uses such as regular-expression patterns.
Write a digit pattern as a raw string, r"\d+", so its backslash reaches the regex engine.
Record key
A record key is a column or combination of columns that identifies one real record, defining which repetitions need investigation.
An order ID is a record key, while a customer ID can legitimately occur on several separate orders.
Regular expression
A regular expression is a text pattern built from character rules and repetition, used to test, extract, or replace structured text.
A regular expression can find a whole-number mass followed by "kg" even when the spacing varies.
rename
The rename method changes row or column labels through a mapping or function while leaving the stored data values untouched.
Use rename(columns={"Weight": "mass_kg"}) to give a heading a clear meaning without editing its cells.
replace
The replace method substitutes exact matching values while leaving unmatched entries unchanged, using a scalar, list, or dictionary.
A replace dictionary can change "pending" to "open" without altering the other status values.
reset_index
The reset_index method moves one or more index levels into ordinary columns and supplies an index for the remaining arrangement.
Use reset_index(level="year") to restore year as a column while leaving city in the index.
Reshaping
Reshaping changes how values and their identifying labels are arranged across rows and columns without itself choosing an aggregation.
Reshaping a complete city/year Series into a wide table preserves each value at its city/year combination.

S

Source key
A source key supplied to concat becomes an outer index label identifying the part from which each row or column originated.
A source key named "batch_a" lets you retrieve those rows even when inner row numbers repeat across batches.
stack
The stack method moves a column-label level into an inner row-index level, returning a Series or DataFrame according to the levels left.
Calling stack on a city table with year columns gives city/year index pairs for the same sales values.
str accessor
The str accessor exposes vectorized text methods on a Series, allowing each non-missing string to be cleaned or searched.
Use the str accessor in s.str.lower() to lowercase an entire city column in one expression.
String splitting
String splitting separates text at a chosen delimiter, producing lists of pieces or separate columns when expansion is requested.
String splitting at ", " turns "Silva, Ana" into a family-name part and a given-name part.
subset
The subset argument limits a duplicate or missing-data check to named columns, leaving other columns out of that decision.
Use subset="order_id" to detect repeated order keys even when their recorded weights differ.
Suffix
A suffix is text appended to overlapping non-key column names during a combination, keeping the source of each column distinguishable.
A suffix such as _sensor keeps one reading column distinct from the same-named column in a backup table.

T

Type conversion
Type conversion changes how values are stored and interpreted, such as turning digit text into numbers for arithmetic.
Type conversion is needed before the text "7.5" can be used as a measured mass in a numeric calculation.

U

Uniqueness
Uniqueness means that no checked key value or key combination repeats, a requirement for one-row-per-key lookup tables.
Check uniqueness on the customer lookup before attaching its details to a table with many orders.
unstack
The unstack method moves a row-index level into columns, requiring unique complete index labels to place one value in each output cell.
Use unstack("year") to put each year into a separate sales column for every city.
Unused category
An unused category is an allowed categorical label with no current observations, which remains in counts until removed from the list.
An unused category L appears with count 0 when a size column currently contains only S and M.

V

Vectorized operation
A vectorized operation acts on a whole Series or array, expressing the same calculation across values without a Python loop per item.
Multiplying the kilometer column by 1000 is a vectorized operation that produces distances in meters.

W

Whitespace
Whitespace consists of spacing characters such as spaces and tabs, which can be meaningful inside text or unwanted around a label.
Outer whitespace makes "Lima " differ from "Lima" until the city values are trimmed with str.strip().
Wide format
Wide format spreads measurements across several columns, often giving each entity one row and each measurement label its own column.
Wide format places three annual sales figures beside each city for direct comparison across years.

Z

Z-score
A z-score expresses a value as its distance above or below the mean divided by the standard deviation used for that column.
A z-score of -1 places a reading one standard deviation below the mean, not in a named interval.

About this lesson

By StudeliReport a mistake

Make your own Study Materials

Turn your notes, slides and documents into Study Guides, Practice Questions, Flashcards and 6 more tools.

Get Started