{"checkpoints":{"s-pda-1":[{"q":"<p>What does this print?</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"python\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Python</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">prices </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Desk Mat XL\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">29</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Laptop Stand\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">49</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">prices[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Desk Mat XL\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> prices.get(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Desk Mat XL\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 1</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">len</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(prices), prices[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Desk Mat XL\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">])</span></span></code></pre></div>\n","options":[{"text":"<code dir=\"ltr\">2 30</code>","why":"Correct. The key already exists, so <code dir=\"ltr\">.get()</code> returns 29, one is added, and the dictionary still has two keys."},{"text":"<code dir=\"ltr\">3 30</code>","why":"Assigning to an existing key updates it; it does not add a second entry with the same name."},{"text":"<code dir=\"ltr\">2 1</code>","why":"<code dir=\"ltr\">.get()</code> returns the stored value, 29, because the key exists. The default 0 is only used for missing keys."},{"text":"A <code dir=\"ltr\">KeyError</code>","why":"Every key the code reads with square brackets exists, and <code dir=\"ltr\">.get()</code> never raises for a missing key anyway."}],"answer":0},{"q":"<p>A colleague’s function returns <code dir=\"ltr\">3.3333333333333335</code> for <code dir=\"ltr\">average([10, 20, 30])</code> instead of <code dir=\"ltr\">20.0</code>. What is the bug?</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"python\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">Python</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">def</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> average</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(values):</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    total </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    for</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> v </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">in</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> values:</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        total </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> v</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> total </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">/</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> len</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(values)</span></span></code></pre></div>\n","options":[{"text":"<code dir=\"ltr\">total</code> should start at 1.","why":"Starting at 1 would make every average slightly too high; it would not explain a result based on the first value only."},{"text":"<code dir=\"ltr\">len(values)</code> counts characters instead of items.","why":"<code dir=\"ltr\">len</code> of a list counts its items, here 3."},{"text":"The <code dir=\"ltr\">return</code> is indented inside the loop, so the function returns after adding the first value.","why":"Correct. On the first pass <code dir=\"ltr\">total</code> is 10, and the function immediately returns 10 / 3. Moving <code dir=\"ltr\">return</code> back to the function’s level fixes it."},{"text":"Python cannot divide integers, so it returns the first value.","why":"Python divides integers with <code dir=\"ltr\">/</code> and returns a float."}],"answer":2},{"q":"<p>Which f-string prints <code dir=\"ltr\">Share: 12.5%</code> when <code dir=\"ltr\">share = 0.125</code>?</p>","options":[{"text":"<code dir=\"ltr\">f&quot;Share: {share:.1f}%&quot;</code>","why":"This formats 0.125 with one decimal, giving <code dir=\"ltr\">0.1%</code>. The <code dir=\"ltr\">f</code> spec does not multiply by 100."},{"text":"<code dir=\"ltr\">f&quot;Share: {share:%}&quot;</code>","why":"The <code dir=\"ltr\">%</code> spec multiplies by 100 but defaults to six decimals: <code dir=\"ltr\">12.500000%</code>."},{"text":"<code dir=\"ltr\">f&quot;Share: {share:,.2f}%&quot;</code>","why":"This prints <code dir=\"ltr\">0.12%</code>: two decimals of the raw fraction."},{"text":"<code dir=\"ltr\">f&quot;Share: {share:.1%}&quot;</code>","why":"Correct. The <code dir=\"ltr\">%</code> spec multiplies by 100 and adds the percent sign; <code dir=\"ltr\">.1</code> keeps one decimal."}],"answer":3},{"q":"<p>A loop over rows stops with <code dir=\"ltr\">TypeError: unsupported operand type(s) for +: 'int' and 'str'</code> on the line <code dir=\"ltr\">total = total + row[&quot;quantity&quot;]</code>. What is the most likely fix?</p>","options":[{"text":"Rename <code dir=\"ltr\">total</code> to something else.","why":"The name is fine; the error is about the types of the two values being added."},{"text":"Convert the quantity first: <code dir=\"ltr\">total = total + int(row[&quot;quantity&quot;])</code>.","why":"Correct. The quantity arrived as text, and Python never adds text to a number implicitly."},{"text":"Wrap the line in <code dir=\"ltr\">try</code> / <code dir=\"ltr\">except TypeError</code> and skip the row.","why":"Every quantity arrived as text, so every row would be skipped and the total would stay 0, with no error to warn you."},{"text":"Start <code dir=\"ltr\">total</code> at <code dir=\"ltr\">&quot;&quot;</code> instead of 0.","why":"That would concatenate the quantities as text, giving something like <code dir=\"ltr\">&quot;121&quot;</code> instead of a number."}],"answer":1},{"q":"<p><code dir=\"ltr\">totals = [65.0, 18.0, 72.0]</code>. What does <code dir=\"ltr\">[t &gt; 50 for t in totals]</code> produce?</p>","options":[{"text":"<code dir=\"ltr\">[True, False, True]</code>","why":"Correct. The expression before <code dir=\"ltr\">for</code> is a comparison, so each item becomes a boolean."},{"text":"<code dir=\"ltr\">[65.0, 72.0]</code>","why":"That is the filtering form, <code dir=\"ltr\">[t for t in totals if t &gt; 50]</code>, where the condition goes after the <code dir=\"ltr\">for</code>."},{"text":"<code dir=\"ltr\">2</code>","why":"Counting needs <code dir=\"ltr\">sum(t &gt; 50 for t in totals)</code>; a list comprehension always builds a list."},{"text":"A <code dir=\"ltr\">SyntaxError</code>","why":"Any expression can go before <code dir=\"ltr\">for</code>, including a comparison."}],"answer":0}],"s-pda-2":[{"q":"<p>After <code dir=\"ltr\">orders = pd.read_csv(&quot;orders.csv&quot;)</code>, <code dir=\"ltr\">orders[&quot;order_date&quot;].dt.month</code> raises <code dir=\"ltr\">AttributeError: Can only use .dt accessor with datetimelike values</code>. What is the fix?</p>","options":[{"text":"Use <code dir=\"ltr\">orders[&quot;order_date&quot;].month</code> instead.","why":"Datetime parts on a column always go through <code dir=\"ltr\">.dt</code>; without it you get another <code dir=\"ltr\">AttributeError</code>."},{"text":"Sort the column first.","why":"Sorting text dates does not change their type."},{"text":"Load with <code dir=\"ltr\">parse_dates=[&quot;order_date&quot;]</code> (or convert with <code dir=\"ltr\">pd.to_datetime</code>) so the column is <code dir=\"ltr\">datetime64</code>.","why":"Correct. The column was read as text (<code dir=\"ltr\">object</code>), and <code dir=\"ltr\">.dt</code> only works on datetime columns."},{"text":"Drop the rows with missing dates.","why":"There are no missing dates; the problem is the column’s type."}],"answer":2},{"q":"<p>What does <code dir=\"ltr\">products.loc[products[&quot;unit_price&quot;] &gt; 100, &quot;name&quot;]</code> return?</p>","options":[{"text":"A DataFrame with every column for products above 100 dollars","why":"Naming a single column after the comma returns just that column."},{"text":"A Series of the names of products priced above 100, indexed by their row labels","why":"Correct. The mask picks the rows and <code dir=\"ltr\">&quot;name&quot;</code> picks one column, so the result is a Series."},{"text":"<code dir=\"ltr\">True</code> or <code dir=\"ltr\">False</code> for each product","why":"That is the mask itself, <code dir=\"ltr\">products[&quot;unit_price&quot;] &gt; 100</code>, before it is used to select."},{"text":"The number of products priced above 100","why":"Counting would need <code dir=\"ltr\">.sum()</code> on the mask or <code dir=\"ltr\">len()</code> on the result."}],"answer":1},{"q":"<p>Which line raises the price of active products by 5% and reliably updates <code dir=\"ltr\">products</code> itself?</p>","options":[{"text":"<code dir=\"ltr\">products[products[&quot;is_active&quot;] == 1][&quot;unit_price&quot;] *= 1.05</code>","why":"Chained assignment: the filter creates a new table, and the change is applied to that copy only."},{"text":"<code dir=\"ltr\">products[&quot;unit_price&quot;][products[&quot;is_active&quot;] == 1] *= 1.05</code>","why":"Also chained; under Copy-on-Write it never reaches <code dir=\"ltr\">products</code>."},{"text":"<code dir=\"ltr\">products.unit_price * 1.05</code>","why":"This computes new prices for every product and throws the result away."},{"text":"<code dir=\"ltr\">products.loc[products[&quot;is_active&quot;] == 1, &quot;unit_price&quot;] *= 1.05</code>","why":"Correct. One <code dir=\"ltr\">.loc</code> call names the rows and the column, so the assignment happens on <code dir=\"ltr\">products</code>."}],"answer":3},{"q":"<p>What is the result of <code dir=\"ltr\">s1 + s2</code>?</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"python\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">Python</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">s1 </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.Series([</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">10</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">20</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">], </span><span style=\"color:#953800;--shiki-dark:#FFA657\">index</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"a\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"b\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">])</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">s2 </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> pd.Series([</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">], </span><span style=\"color:#953800;--shiki-dark:#FFA657\">index</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">[</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"b\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"c\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">])</span></span></code></pre></div>\n","options":[{"text":"<code dir=\"ltr\">a: NaN, b: 21, c: NaN</code>","why":"Correct. Values are matched by label; only <code dir=\"ltr\">b</code> exists on both sides, and labels on one side only give <code dir=\"ltr\">NaN</code>."},{"text":"<code dir=\"ltr\">a: 11, b: 22</code>","why":"That is position-based addition, which pandas does not do for Series with different labels."},{"text":"<code dir=\"ltr\">a: 10, b: 21, c: 2</code>","why":"Missing partners are not treated as zero; use <code dir=\"ltr\">s1.add(s2, fill_value=0)</code> for that behaviour."},{"text":"A <code dir=\"ltr\">ValueError</code> because the indexes differ","why":"pandas aligns differing indexes instead of raising."}],"answer":0},{"q":"<p>You want every order from the web or the mobile app. Which filter is the clearest correct choice?</p>","options":[{"text":"<code dir=\"ltr\">orders[orders[&quot;channel&quot;] == &quot;web&quot; or orders[&quot;channel&quot;] == &quot;mobile_app&quot;]</code>","why":"<code dir=\"ltr\">or</code> needs one True/False, so pandas raises <code dir=\"ltr\">ValueError: The truth value of a Series is ambiguous</code>."},{"text":"<code dir=\"ltr\">orders[orders[&quot;channel&quot;] == [&quot;web&quot;, &quot;mobile_app&quot;]]</code>","why":"Comparing a column with a list does element-wise comparison against a list of a different length, which raises an error."},{"text":"<code dir=\"ltr\">orders[orders[&quot;channel&quot;].isin([&quot;web&quot;, &quot;mobile_app&quot;])]</code>","why":"Correct. <code dir=\"ltr\">isin</code> checks each value against the list and reads exactly like the question."},{"text":"<code dir=\"ltr\">orders.loc[&quot;web&quot;:&quot;mobile_app&quot;]</code>","why":"That slices row labels, not channel values; the index here is the row number."}],"answer":2}],"s-pda-3":[{"q":"<p>A quantity column loads as <code dir=\"ltr\">float64</code> and shows values like <code dir=\"ltr\">1.0</code> and <code dir=\"ltr\">2.0</code>. What explains it, and what keeps the column as whole numbers?</p>","options":[{"text":"Some quantities are fractions; round them with <code dir=\"ltr\">round()</code>.","why":"Quantities here are whole numbers; the type comes from blanks, not fractions."},{"text":"Some cells are blank, classic <code dir=\"ltr\">NaN</code> is a float, and <code dir=\"ltr\">astype(&quot;Int64&quot;)</code> keeps integers with <code dir=\"ltr\">&lt;NA&gt;</code> for the gaps.","why":"Correct. The nullable <code dir=\"ltr\">Int64</code> dtype was made for exactly this case."},{"text":"<code dir=\"ltr\">read_csv</code> always loads numbers as floats; use <code dir=\"ltr\">astype(int)</code>.","why":"Columns without gaps load as <code dir=\"ltr\">int64</code>; and <code dir=\"ltr\">astype(int)</code> fails while <code dir=\"ltr\">NaN</code> values are present."},{"text":"The file is corrupted; reload it with <code dir=\"ltr\">keep_default_na=False</code>.","why":"That keeps blanks as empty strings, turning the whole column into text."}],"answer":1},{"q":"<p><code dir=\"ltr\">pd.to_numeric(cleaned, errors=&quot;coerce&quot;)</code> produced 12 new <code dir=\"ltr\">NaN</code> values. What should you do next?</p>","options":[{"text":"Use <code dir=\"ltr\">fillna(0)</code> so totals are complete.","why":"That turns 12 unknown values into real-looking zeros and understates revenue."},{"text":"Drop the 12 rows.","why":"They may be real sales in an unexpected format; dropping them loses revenue without knowing why."},{"text":"Switch to <code dir=\"ltr\">astype(float)</code>, which is stricter.","why":"It would raise on the first bad value, which tells you less than listing them all."},{"text":"Print the original strings for those rows and extend the cleaning rule for each format you find.","why":"Correct. Coercion is a way to find problem values, and the raw strings tell you what rule is missing."}],"answer":3},{"q":"<p>What does <code dir=\"ltr\">pd.to_datetime([&quot;03/04/2025&quot;, &quot;15/04/2025&quot;], format=&quot;%d/%m/%Y&quot;)</code> return?</p>","options":[{"text":"3 April 2025 and 15 April 2025","why":"Correct. The format says day first, so both parse, and the explicit format removes any guessing."},{"text":"4 March 2025 and an error","why":"That would be the month-first reading, <code dir=\"ltr\">%m/%d/%Y</code>, which fails on month 15."},{"text":"4 March 2025 and 15 April 2025","why":"With an explicit format every value is read the same way; pandas does not switch per value."},{"text":"Two <code dir=\"ltr\">NaT</code> values","why":"<code dir=\"ltr\">NaT</code> appears only with <code dir=\"ltr\">errors=&quot;coerce&quot;</code> for values that do not match; both values match."}],"answer":0},{"q":"<p>Your cleaning function removes duplicates first and strips spaces from names afterwards. What can go wrong?</p>","options":[{"text":"Nothing; the order of steps does not matter.","why":"Duplicate detection compares exact values, so order matters."},{"text":"<code dir=\"ltr\">strip()</code> will remove letters from the names.","why":"<code dir=\"ltr\">strip()</code> removes only leading and trailing whitespace."},{"text":"Copies that differ only by spaces (<code dir=\"ltr\">&quot;Ali Evans&quot;</code> and <code dir=\"ltr\">&quot;Ali Evans &quot;</code>) are not recognised as duplicates and both survive.","why":"Correct. Clean the values first, then look for duplicates."},{"text":"<code dir=\"ltr\">drop_duplicates</code> will raise an error on text columns.","why":"It works on any column type."}],"answer":2},{"q":"<p>After <code dir=\"ltr\">df[&quot;country&quot;] = df[&quot;country&quot;].str.title()</code>, <code dir=\"ltr\">nunique()</code> still reports 9 countries instead of 8. What is the most likely cause?</p>","options":[{"text":"<code dir=\"ltr\">title()</code> does not work on capitals such as <code dir=\"ltr\">EGYPT</code>.","why":"<code dir=\"ltr\">&quot;EGYPT&quot;.title()</code> gives <code dir=\"ltr\">&quot;Egypt&quot;</code>; case is handled."},{"text":"Some values have leading or trailing spaces, so <code dir=\"ltr\">&quot;  Egypt&quot;</code> and <code dir=\"ltr\">&quot;Egypt&quot;</code> are still different.","why":"Correct. <code dir=\"ltr\">str.strip()</code> must come before (or with) the case fix."},{"text":"<code dir=\"ltr\">nunique()</code> counts missing values as a country.","why":"<code dir=\"ltr\">nunique()</code> ignores missing values by default."},{"text":"pandas caches the old values until the file is reloaded.","why":"Assignment replaces the column immediately; nothing is cached."}],"answer":1}],"s-pda-4":[{"q":"<p>You left-merge <code dir=\"ltr\">order_items</code> (3,991 rows) with <code dir=\"ltr\">returns</code> on <code dir=\"ltr\">order_item_id</code> with <code dir=\"ltr\">indicator=True</code>. 198 rows are <code dir=\"ltr\">both</code>. What do the <code dir=\"ltr\">left_only</code> rows represent?</p>","options":[{"text":"Returns that have no matching order line","why":"Those would be <code dir=\"ltr\">right_only</code>, and a left join never keeps them anyway."},{"text":"Order lines that were returned twice","why":"A line returned twice would produce two <code dir=\"ltr\">both</code> rows, not a <code dir=\"ltr\">left_only</code> one."},{"text":"Rows the merge failed to process","why":"Merges do not fail silently; <code dir=\"ltr\">left_only</code> is a normal, meaningful outcome."},{"text":"The 3,793 order lines that were never returned","why":"Correct. Every item stays, and those without a matching return are marked <code dir=\"ltr\">left_only</code>."}],"answer":3},{"q":"<p><code dir=\"ltr\">sales.pivot_table(index=&quot;channel&quot;, columns=&quot;year&quot;, values=&quot;revenue&quot;)</code> returns numbers far smaller than the revenue totals. Why?</p>","options":[{"text":"The default <code dir=\"ltr\">aggfunc</code> is <code dir=\"ltr\">&quot;mean&quot;</code>, so each cell is the average line revenue, not the sum.","why":"Correct. Pass <code dir=\"ltr\">aggfunc=&quot;sum&quot;</code> for totals; always state the aggregation explicitly."},{"text":"pivot_table drops cancelled orders automatically.","why":"It never filters by status; it only aggregates what it is given."},{"text":"The values are in thousands of dollars.","why":"pandas never rescales values on its own."},{"text":"Rows with missing years were dropped.","why":"Dropping a few rows could not shrink every cell this much, and there are no missing years."}],"answer":0},{"q":"<p><code dir=\"ltr\">orders.resample(&quot;ME&quot;)[&quot;revenue&quot;].sum()</code> raises <code dir=\"ltr\">TypeError: Only valid with DatetimeIndex, TimedeltaIndex or PeriodIndex</code>. What is the fix?</p>","options":[{"text":"Use <code dir=\"ltr\">&quot;M&quot;</code> instead of <code dir=\"ltr\">&quot;ME&quot;</code>.","why":"The alias is fine; <code dir=\"ltr\">&quot;M&quot;</code> is the deprecated spelling and would hit the same error."},{"text":"Sort the orders by date first.","why":"Sorting does not give the table a datetime index."},{"text":"Put the dates in the index with <code dir=\"ltr\">set_index(&quot;order_date&quot;)</code>, or pass <code dir=\"ltr\">on=&quot;order_date&quot;</code> to <code dir=\"ltr\">resample</code>.","why":"Correct. resample needs to know which datetime values to bucket."},{"text":"Convert revenue to integers.","why":"The error is about the index, not the values being summed."}],"answer":2},{"q":"<p>Merchandising wants products split into five groups with the same number of products in each, by price. Which call fits?</p>","options":[{"text":"<code dir=\"ltr\">pd.cut(products[&quot;unit_price&quot;], 5)</code>","why":"This makes five ranges of equal width; with a few expensive products, most items land in the first group."},{"text":"<code dir=\"ltr\">pd.qcut(products[&quot;unit_price&quot;], 5)</code>","why":"Correct. <code dir=\"ltr\">qcut</code> cuts at quantiles, so each group holds about a fifth of the products."},{"text":"<code dir=\"ltr\">products[&quot;unit_price&quot;].rank(5)</code>","why":"<code dir=\"ltr\">rank</code> numbers rows; its first argument is not a number of groups."},{"text":"<code dir=\"ltr\">products.groupby(&quot;unit_price&quot;).ngroups</code>","why":"This counts distinct prices; it does not assign products to groups."}],"answer":1},{"q":"<p>Which call returns revenue per country with <code dir=\"ltr\">country</code> as an ordinary column rather than the index?</p>","options":[{"text":"<code dir=\"ltr\">sales.groupby(&quot;country&quot;)[&quot;revenue&quot;].sum()</code>","why":"This returns a Series indexed by country."},{"text":"<code dir=\"ltr\">sales.groupby(&quot;country&quot;).sum(&quot;revenue&quot;)</code>","why":"This sums every numeric column and still indexes by country."},{"text":"<code dir=\"ltr\">sales.pivot(index=&quot;country&quot;, values=&quot;revenue&quot;)</code>","why":"<code dir=\"ltr\">pivot</code> needs a <code dir=\"ltr\">columns</code> argument and never aggregates, so it fails here, and would still fail on repeated countries."},{"text":"<code dir=\"ltr\">sales.groupby(&quot;country&quot;, as_index=False)[&quot;revenue&quot;].sum()</code>","why":"Correct. <code dir=\"ltr\">as_index=False</code> keeps the group key as a column, ready for merging or exporting."}],"answer":3}],"s-pda-5":[{"q":"<p>Web sessions view a mean of 4.2 pages and a median of 3. A product manager asks for the “typical” session. What do you report?</p>","options":[{"text":"4.2 pages, because the mean uses every session","why":"The mean is pulled up by a few long sessions, so it describes no typical visitor."},{"text":"Both numbers averaged, 3.6 pages","why":"Averaging two statistics produces a number with no meaning."},{"text":"3 pages, the median, ideally with a range such as the quartiles","why":"Correct. In skewed data the median is the typical value, and quartiles show the spread."},{"text":"The maximum, to show what is possible","why":"The maximum describes one extreme session, not a typical one."}],"answer":2},{"q":"<p><code dir=\"ltr\">added_to_cart</code> and <code dir=\"ltr\">purchased</code> have a correlation of 0.62 across sessions. Which conclusion is sound?</p>","options":[{"text":"Sessions that add to cart are much more likely to end in a purchase, which makes the cart a useful funnel step to track.","why":"Correct. The correlation describes a strong association; a purchase normally requires a cart, so the pattern is expected."},{"text":"Adding to cart causes 62% of purchases.","why":"A correlation coefficient is not a percentage of outcomes, and it says nothing about cause by itself."},{"text":"62% of sessions add to cart.","why":"That would be the mean of <code dir=\"ltr\">added_to_cart</code>, a different number."},{"text":"The two columns are unrelated.","why":"0.62 indicates a fairly strong positive relationship."}],"answer":0},{"q":"<p>Month A to month B: revenue +20%, orders +20%, average order value unchanged. What drove the growth?</p>","options":[{"text":"Bigger baskets","why":"Average order value did not change, so basket size cannot explain anything here."},{"text":"Higher prices","why":"Higher prices would show up as a higher average order value, which stayed flat."},{"text":"It cannot be determined without customer data.","why":"The decomposition already answers it at this level; customer data would split it further."},{"text":"More orders at the same average size","why":"Correct. Revenue = orders × average order value, and only orders moved."}],"answer":3},{"q":"<p>You need to show eight quarters of revenue so the board can compare their sizes. Which chart?</p>","options":[{"text":"A pie chart with eight slices","why":"Pies hide time order and make eight similar slices hard to compare."},{"text":"Vertical bars in time order, starting at zero","why":"Correct. Bars compare amounts, time order shows the trend, and a zero baseline keeps lengths honest."},{"text":"A line chart with the y-axis starting at the smallest quarter","why":"Zooming hides how big each quarter is; for comparing amounts, start at zero."},{"text":"A histogram of quarterly revenue","why":"A histogram shows how values are distributed, losing which quarter is which."}],"answer":1},{"q":"<p>Restart and Run All stops with <code dir=\"ltr\">NameError: name 'clean_orders' is not defined</code>, although the notebook worked an hour ago. What happened?</p>","options":[{"text":"The cell defining <code dir=\"ltr\">clean_orders</code> was deleted or moved below the cell that uses it; the function only existed in the old session.","why":"Correct. Hidden state hid the problem; define functions before using them, or import them from a <code dir=\"ltr\">.py</code> file."},{"text":"pandas was upgraded during the hour.","why":"An upgrade would not make your own function name undefined."},{"text":"The CSV file was renamed.","why":"A missing file raises <code dir=\"ltr\">FileNotFoundError</code>, not <code dir=\"ltr\">NameError</code>."},{"text":"Restart and Run All does not support functions.","why":"It runs every cell in order, including function definitions."}],"answer":0}]},"final":[{"q":"<p>What does <code dir=\"ltr\">sum(float(v.replace(&quot;,&quot;, &quot;&quot;)) for v in [&quot;1,234.50&quot;, &quot;99.00&quot;])</code> return?</p>","options":[{"text":"<code dir=\"ltr\">&quot;1,234.5099.00&quot;</code>","why":"The values are converted to floats before summing, so no strings are joined."},{"text":"A <code dir=\"ltr\">ValueError</code>","why":"The commas are removed before <code dir=\"ltr\">float()</code> sees the text, so both conversions succeed."},{"text":"<code dir=\"ltr\">1333.5</code>","why":"Correct. Each string is cleaned and converted, then the floats are added."},{"text":"<code dir=\"ltr\">1234.5</code>","why":"The generator covers both values, so 99.0 is added too."}],"answer":2},{"q":"<p><code dir=\"ltr\">cancelled = orders[orders[&quot;status&quot;] == &quot;cancelled&quot;]</code> on Cartwheel’s orders. What is <code dir=\"ltr\">cancelled.shape</code>?</p>","options":[{"text":"<code dir=\"ltr\">(2066, 7)</code>","why":"A filter keeps only matching rows; the full table has 2,066."},{"text":"<code dir=\"ltr\">(105, 7)</code>","why":"Correct. 105 orders were cancelled, and filtering rows keeps all 7 columns."},{"text":"<code dir=\"ltr\">(105,)</code>","why":"That would be the shape of a Series; filtering a DataFrame returns a DataFrame."},{"text":"<code dir=\"ltr\">(7, 105)</code>","why":"Shape is always rows first, then columns."}],"answer":1},{"q":"<p><code dir=\"ltr\">messy[&quot;total&quot;].sum()</code> returns a long string instead of a number. Which first step diagnoses it?</p>","options":[{"text":"<code dir=\"ltr\">messy[&quot;total&quot;].round(2)</code>","why":"Rounding text is not possible; it raises a <code dir=\"ltr\">TypeError</code> rather than explaining anything."},{"text":"<code dir=\"ltr\">messy.drop_duplicates()</code>","why":"Duplicates change the size of a sum, not its type."},{"text":"<code dir=\"ltr\">messy[&quot;total&quot;].fillna(0)</code>","why":"Filling blanks does not turn text into numbers."},{"text":"<code dir=\"ltr\">messy[&quot;total&quot;].dtype</code> and a look at a few values, which reveal text such as <code dir=\"ltr\">&quot;$65.00&quot;</code>","why":"Correct. An <code dir=\"ltr\">object</code> dtype on a number column means some values contain non-numeric characters."}],"answer":3},{"q":"<p>Support tickets can reference an order, and one order can have several tickets. Which <code dir=\"ltr\">validate</code> fits <code dir=\"ltr\">orders.merge(tickets, on=&quot;order_id&quot;)</code>?</p>","options":[{"text":"<code dir=\"ltr\">&quot;one_to_many&quot;</code>","why":"Correct. Each order id appears once in <code dir=\"ltr\">orders</code> and possibly several times in <code dir=\"ltr\">tickets</code>."},{"text":"<code dir=\"ltr\">&quot;many_to_one&quot;</code>","why":"That asserts the right-hand keys are unique; ticket order ids repeat, so it would raise <code dir=\"ltr\">MergeError</code>."},{"text":"<code dir=\"ltr\">&quot;one_to_one&quot;</code>","why":"Ticket order ids repeat, so this raises."},{"text":"No validation is possible for this pair of tables.","why":"Validation works for any merge; you choose the relationship you expect."}],"answer":0},{"q":"<p>You need the number of orders per customer, but only <code dir=\"ltr\">order_items</code> (one row per line) is at hand. Which aggregation is right?</p>","options":[{"text":"<code dir=\"ltr\">items.groupby(&quot;customer_id&quot;).size()</code>","why":"<code dir=\"ltr\">order_items</code> has no <code dir=\"ltr\">customer_id</code>, and <code dir=\"ltr\">size()</code> on lines would count lines, not orders."},{"text":"<code dir=\"ltr\">items.groupby(&quot;order_id&quot;).size()</code>","why":"This counts lines per order, not orders per customer."},{"text":"Merge <code dir=\"ltr\">customer_id</code> from <code dir=\"ltr\">orders</code>, then <code dir=\"ltr\">groupby(&quot;customer_id&quot;)[&quot;order_id&quot;].nunique()</code>","why":"Correct. Distinct order ids per customer count orders, however many lines each has."},{"text":"<code dir=\"ltr\">items[&quot;order_id&quot;].value_counts()</code>","why":"This is lines per order again."}],"answer":2},{"q":"<p><code dir=\"ltr\">monthly</code> is revenue per month for 2024 and 2025. Which expression compares each month with the same month a year earlier?</p>","options":[{"text":"<code dir=\"ltr\">monthly.pct_change()</code>","why":"This compares each month with the one before it, mixing in seasonality."},{"text":"<code dir=\"ltr\">monthly.pct_change(12)</code>","why":"Correct. Twelve periods back is the same month last year."},{"text":"<code dir=\"ltr\">monthly.rolling(12).mean()</code>","why":"A rolling mean smooths the series; it does not compare periods."},{"text":"<code dir=\"ltr\">monthly.diff()</code>","why":"<code dir=\"ltr\">diff</code> gives month-on-month changes in dollars."}],"answer":1},{"q":"<p>What label does <code dir=\"ltr\">pd.cut([100], bins=[0, 100, 200])</code> give to the value 100?</p>","options":[{"text":"<code dir=\"ltr\">(100, 200]</code>","why":"Bins are closed on the right by default, so 100 belongs to the bin that ends at 100."},{"text":"<code dir=\"ltr\">NaN</code>","why":"100 lies inside the edges, so it is labelled."},{"text":"<code dir=\"ltr\">[100, 200)</code>","why":"A left-closed bin like that needs <code dir=\"ltr\">right=False</code>; the default closes bins on the right."},{"text":"<code dir=\"ltr\">(0, 100]</code>","why":"Correct. With the default <code dir=\"ltr\">right=True</code>, each bin includes its right edge."}],"answer":3},{"q":"<p>One order of 50,000 dollars is added by mistake to a table of 2,000 normal orders. Which statistic barely changes?</p>","options":[{"text":"The median","why":"Correct. One extreme value moves the middle position by at most one row, so the median hardly moves."},{"text":"The mean","why":"The mean rises by about 25 dollars, a large shift for a typical order of a few hundred."},{"text":"The standard deviation","why":"A single extreme value inflates the standard deviation considerably."},{"text":"The maximum","why":"The maximum jumps straight to the mistaken 50,000-dollar order."}],"answer":0},{"q":"<p><code dir=\"ltr\">q4 = orders[in_q4]</code> followed by <code dir=\"ltr\">q4[&quot;flag&quot;] = True</code>. What happens to <code dir=\"ltr\">orders</code> under Copy-on-Write?</p>","options":[{"text":"<code dir=\"ltr\">orders</code> gains a <code dir=\"ltr\">flag</code> column with <code dir=\"ltr\">True</code> for Q4 rows.","why":"A filtered table behaves as a copy; changes to it never flow back."},{"text":"pandas raises an error because <code dir=\"ltr\">q4</code> is a view.","why":"Assigning a new column to <code dir=\"ltr\">q4</code> is allowed; it just affects <code dir=\"ltr\">q4</code> only."},{"text":"Nothing; only <code dir=\"ltr\">q4</code> gets the new column.","why":"Correct. To mark rows inside <code dir=\"ltr\">orders</code>, use <code dir=\"ltr\">orders.loc[in_q4, &quot;flag&quot;] = True</code>."},{"text":"<code dir=\"ltr\">orders</code> gets a <code dir=\"ltr\">flag</code> column filled with <code dir=\"ltr\">True</code> for every row.","why":"Nothing is written to <code dir=\"ltr\">orders</code> at all."}],"answer":2},{"q":"<p><code dir=\"ltr\">df[&quot;order_date&quot;].str.contains(&quot;2025&quot;)</code> works, but <code dir=\"ltr\">df[&quot;order_date&quot;].dt.year</code> fails. What does that tell you?</p>","options":[{"text":"The column holds datetimes with a time zone.","why":"Datetime columns support <code dir=\"ltr\">.dt.year</code>, with or without a time zone."},{"text":"The column is still text and needs <code dir=\"ltr\">pd.to_datetime</code> before date operations.","why":"Correct. <code dir=\"ltr\">.str</code> working and <code dir=\"ltr\">.dt</code> failing is the signature of dates stored as strings."},{"text":"The dates are ambiguous.","why":"Ambiguity affects how text is parsed; it does not stop a datetime column from using <code dir=\"ltr\">.dt</code>."},{"text":"The column has missing values.","why":"Missing values become <code dir=\"ltr\">NaT</code> in a datetime column and do not break <code dir=\"ltr\">.dt</code>."}],"answer":1},{"q":"<p>A quarter’s revenue rose 30%. Active customers rose 30%; orders per customer and average order value were flat. What is the best summary?</p>","options":[{"text":"Customers spent more per order.","why":"Average order value was flat."},{"text":"Customers ordered more often.","why":"Orders per customer were flat."},{"text":"Prices went up.","why":"Price rises would raise the average order value, which did not change."},{"text":"Growth came from having more active customers, each behaving as before.","why":"Correct. Revenue = customers × orders per customer × average order value, and only customers moved."}],"answer":3},{"q":"<p>Which habit most protects a recurring monthly report from silently reporting wrong numbers when next month’s export changes format?</p>","options":[{"text":"A cleaning function ending in assertions about ids, dates, totals and allowed values","why":"Correct. When the format changes, an assertion stops the run with a message instead of letting bad data reach the report."},{"text":"Rounding every number to whole dollars","why":"Rounding hides small differences; it does not detect a broken input."},{"text":"Running the notebook cells in a different order each month","why":"That makes results less reproducible, not more."},{"text":"Using <code dir=\"ltr\">errors=&quot;coerce&quot;</code> everywhere so nothing crashes","why":"Coercion without checks turns format changes into silent <code dir=\"ltr\">NaN</code>s, the exact failure you want to catch."}],"answer":0}],"glossary":[{"id":"variable","term":"Variable","definition":"A name attached to a value with <code dir=\"ltr\">=</code>, such as <code dir=\"ltr\">unit_price = 39</code>, so the value can be used later; the right side is computed first, then the name points at the result.","lesson":"l-pda-1-2"},{"id":"dtype","term":"dtype","definition":"The data type shared by every value in a pandas column, such as <code dir=\"ltr\">int64</code>, <code dir=\"ltr\">float64</code>, <code dir=\"ltr\">object</code> (text), <code dir=\"ltr\">bool</code> or <code dir=\"ltr\">datetime64</code>; a wrong dtype is behind many data bugs.","lesson":"l-pda-2-1"},{"id":"dictionary","term":"Dictionary","definition":"A Python container of key: value pairs, such as a product’s name mapped to its price; you look values up by key with <code dir=\"ltr\">d[key]</code> or <code dir=\"ltr\">d.get(key, default)</code>.","lesson":"l-pda-1-3"},{"id":"list-comprehension","term":"List comprehension","definition":"A one-line way to build a list, <code dir=\"ltr\">[expr for x in items if condition]</code>, that applies an expression to each item and optionally filters which items are kept.","lesson":"l-pda-1-4"},{"id":"f-string","term":"f-string","definition":"A string literal prefixed with <code dir=\"ltr\">f</code> that embeds values in braces with optional formatting, such as <code dir=\"ltr\">f&quot;{revenue:,.2f}&quot;</code> for 1,234.50 or <code dir=\"ltr\">f&quot;{share:.1%}&quot;</code> for 12.5%.","lesson":"l-pda-1-5"},{"id":"traceback","term":"Traceback","definition":"The report Python prints when an exception stops a program; read it from the bottom, where the last line names the error type and message, then up to the failing line.","lesson":"l-pda-1-5"},{"id":"series","term":"Series","definition":"A one-dimensional pandas array of values with an index of labels; a single column of a DataFrame is a Series.","lesson":"l-pda-2-1"},{"id":"dataframe","term":"DataFrame","definition":"The pandas table: columns that are Series sharing one row index, with methods to load, select, filter, group, join and reshape data.","lesson":"l-pda-2-1"},{"id":"index-alignment","term":"Index alignment","definition":"pandas’ rule that operations between two Series or DataFrames match values by index label, not by position; labels present on only one side produce NaN.","lesson":"l-pda-2-1"},{"id":"loc-iloc","term":"loc and iloc","definition":"The two precise selectors in pandas: <code dir=\"ltr\">.loc[rows, columns]</code> selects by label and includes the end of a slice, while <code dir=\"ltr\">.iloc</code> selects by integer position and excludes it.","lesson":"l-pda-2-3"},{"id":"boolean-mask","term":"Boolean mask","definition":"A Series of True and False values, usually from a comparison, used to select rows with <code dir=\"ltr\">df[mask]</code>; its sum counts matches and its mean gives their share.","lesson":"l-pda-2-4"},{"id":"chained-assignment","term":"Chained assignment","definition":"Assigning through two selections, such as <code dir=\"ltr\">df[&quot;col&quot;][mask] = value</code>; it may change a temporary copy instead of the DataFrame, so use one <code dir=\"ltr\">.loc</code> call instead.","lesson":"l-pda-2-5"},{"id":"copy-on-write","term":"Copy-on-Write","definition":"pandas behaviour, default from version 3.0 and optional in 2.x, in which every selection acts as a copy, so the only way to change a DataFrame is to assign to it directly.","lesson":"l-pda-2-5"},{"id":"nan","term":"NaN","definition":"Short for not a number: the classic pandas marker for a missing value in float columns; it is not equal to anything, so test for it with <code dir=\"ltr\">isna()</code>.","lesson":"l-pda-3-1"},{"id":"nullable-dtype","term":"Nullable dtype","definition":"A pandas dtype such as <code dir=\"ltr\">Int64</code>, <code dir=\"ltr\">Float64</code>, <code dir=\"ltr\">boolean</code> or <code dir=\"ltr\">string</code> that stores missing values as <code dir=\"ltr\">pd.NA</code>, so integer columns stay integers even with gaps.","lesson":"l-pda-3-1"},{"id":"str-accessor","term":"str accessor","definition":"The <code dir=\"ltr\">.str</code> attribute of a text Series that applies string methods such as <code dir=\"ltr\">strip</code>, <code dir=\"ltr\">title</code>, <code dir=\"ltr\">replace</code>, <code dir=\"ltr\">split</code> and <code dir=\"ltr\">contains</code> to every value at once.","lesson":"l-pda-3-2"},{"id":"regular-expression","term":"Regular expression","definition":"A pattern language for text in which characters like <code dir=\"ltr\">$</code> and <code dir=\"ltr\">.</code> have special meanings; pass <code dir=\"ltr\">regex=False</code> to treat them as plain characters.","lesson":"l-pda-3-3"},{"id":"ambiguous-date","term":"Ambiguous date","definition":"A date string such as 05/08/2025 that is valid under both day-first and month-first readings; only outside information can decide which one is correct.","lesson":"l-pda-3-4"},{"id":"assertion","term":"Assertion","definition":"An <code dir=\"ltr\">assert condition, message</code> statement that does nothing when the condition holds and stops the program with the message when it fails; used to state what clean data must look like.","lesson":"l-pda-3-5"},{"id":"groupby","term":"groupby (split-apply-combine)","definition":"The pandas operation that splits rows into groups by key, applies a calculation to each group and combines the results into one table.","lesson":"l-pda-4-1"},{"id":"named-aggregation","term":"Named aggregation","definition":"A form of <code dir=\"ltr\">agg</code> in which each keyword names an output column and its value is a (column, function) pair, such as <code dir=\"ltr\">revenue=(&quot;revenue&quot;, &quot;sum&quot;)</code>.","lesson":"l-pda-4-1"},{"id":"merge","term":"merge","definition":"The pandas method that combines two tables side by side by matching key values; <code dir=\"ltr\">how</code> sets the join type and <code dir=\"ltr\">validate</code> checks the expected key relationship.","lesson":"l-pda-4-2"},{"id":"join-type","term":"Join type","definition":"The rule for unmatched keys in a merge: inner keeps keys found on both sides, left keeps every left row, right every right row, and outer keeps all keys.","lesson":"l-pda-4-2"},{"id":"long-wide","term":"Long and wide data","definition":"Two shapes for the same data: long has one row per observation, wide spreads one variable across columns; <code dir=\"ltr\">pivot_table</code> goes from long to wide and <code dir=\"ltr\">melt</code> back.","lesson":"l-pda-4-3"},{"id":"resample","term":"resample","definition":"A pandas method that groups rows with a datetime index into regular periods such as days, weeks (<code dir=\"ltr\">W</code>), month ends (<code dir=\"ltr\">ME</code>) or quarter ends (<code dir=\"ltr\">QE</code>), including empty periods.","lesson":"l-pda-4-4"},{"id":"year-over-year","term":"Year-over-year growth","definition":"The change compared with the same period a year earlier, such as <code dir=\"ltr\">pct_change(4)</code> on quarterly data; it removes seasonal patterns from the comparison.","lesson":"l-pda-4-4"},{"id":"qcut","term":"qcut","definition":"A pandas function that splits values into groups of about equal size at quantiles, such as quartiles, unlike <code dir=\"ltr\">cut</code>, which uses boundaries you choose.","lesson":"l-pda-4-5"},{"id":"median","term":"Median","definition":"The middle value of a sorted column, with half the values below it; in skewed data such as order values it describes a typical value better than the mean.","lesson":"l-pda-5-1"},{"id":"correlation","term":"Correlation","definition":"A number from -1 to 1 measuring how strongly two numeric columns move together in a straight-line pattern; it signals association, not cause.","lesson":"l-pda-5-1"},{"id":"reproducible-analysis","term":"Reproducible analysis","definition":"An analysis that anyone can rerun from the raw files, top to bottom, with no manual steps, and get the same results, using parameters, functions and assertions.","lesson":"l-pda-5-4"}]}