41. Integers turn into floats in iterrows
df has an int column qty and a float column price. Inside for _, row in df.iterrows(), row["qty"] is 1.0 instead of 1. Why?
- A.iterrows always converts every value to float
- B.Each row becomes one Series with a single dtype
- C.The qty column was stored as float all along
- D.iterrows reads the data from a CSV copy
Show answer
Answer: B. Each row becomes one Series with a single dtype
iterrows builds a Series for every row, and a Series has a single dtype, so int and float values share float64. That can break ID lookups and equality checks. itertuples keeps each column's type and is much faster, and a vectorised operation is better still.
42. First quartile of four values
What does pd.Series([1, 2, 3, 4]).quantile(0.25) return?
- A.1.75
- B.1.5
- C.1.0
- D.2.0
Show answer
Answer: A. 1.75
pandas uses linear interpolation by default. The position is (n - 1) x 0.25 = 0.75, so the result is three quarters of the way from the 1st value (1) to the 2nd value (2), which is 1.75. Other tools such as Excel's QUARTILE.EXC use different rules, so quartiles can differ slightly between tools.
43. NaN keys in a pandas merge
Both tables have one row where the key k is NaN. What does a.merge(b, on="k") do with them?
- A.It drops both rows, like SQL does with NULL
- B.It matches the two NaN keys as a pair
- C.It raises a ValueError about missing keys
- D.It keeps both rows unmatched
Show answer
Answer: B. It matches the two NaN keys as a pair
Unlike SQL, where NULL never equals NULL, pandas merge treats NaN keys as equal and joins them. This can create false matches, for example many orders with an unknown customer joined to an unknown customer row. Drop or fill missing keys before merging when that is not what you want.
44. Sum of a column that is all missing
s = pd.Series([np.nan, np.nan]). What are s.sum() and s.mean()?
- A.NaN and NaN
- B.0.0 and 0.0
- C.0.0 and NaN
- D.It raises an error for both
Show answer
Answer: C. 0.0 and NaN
pandas skips NaN, and the sum of nothing is defined as 0, while the mean of nothing is undefined, so NaN. A store with no data can therefore show ₹0 revenue instead of "no data". Use s.sum(min_count=1) to get NaN when there are no real values.
45. Assigning a Series with a different index
What is in df["s"] after this code?
df = pd.DataFrame({"v": [1, 2, 3]}, index=[0, 1, 2])
s = pd.Series([10, 20, 30], index=[1, 2, 3])
df["s"] = s- A.10, 20, 30
- B.20, 30, NaN
- C.It raises a length mismatch error
- D.NaN, 10, 20
Show answer
Answer: D. NaN, 10, 20
pandas aligns the Series to the DataFrame by index label, not by position. Label 0 has no value, labels 1 and 2 get 10 and 20, and label 3 is dropped. This is a common bug after filtering or sorting; use reset_index or .to_numpy() when you really mean position.
46. Latest exchange rate before each trade
You need the most recent AED to INR rate at or before each trade time. Which tool is built for this?
- A.pd.merge_asof(trades, rates, on="time")
- B.trades.merge(rates, on="time", how="left")
- C.pd.concat([trades, rates]).ffill()
- D.trades.join(rates, on="time", how="outer")
Show answer
Answer: A. pd.merge_asof(trades, rates, on="time")
merge_asof matches each left row with the nearest key on the right, and its default direction="backward" picks the last one at or before the time. Both inputs must be sorted by the key. An exact merge would only match trades at the very second a rate was published.
47. Memory report far below real use
df.info() reports 60 MB, but the Python process uses over 600 MB. Most columns hold Python string objects. What shows the true size?
- A.df.memory_usage(index=False)
- B.df.info(verbose=True)
- C.df.memory_usage(deep=True)
- D.sys.getsizeof(df.values)
Show answer
Answer: C. df.memory_usage(deep=True)
By default pandas counts only the 8-byte pointer for each object value, not the strings they point to. deep=True measures the actual string objects, which is often 10 times more. Converting such columns to category or a PyArrow-backed string type usually cuts memory a lot.
48. Business days for a Saudi office
A Riyadh office works Sunday to Thursday. Which offset from pd.offsets gives its working days correctly?
- A.BDay(), the standard business day offset
- B.CustomBusinessDay(weekmask="Sun Mon Tue Wed Thu")
- C.Week(weekday=6), one step per Sunday
- D.CustomBusinessDay(holidays=["Fri", "Sat"])
Show answer
Answer: B. CustomBusinessDay(weekmask="Sun Mon Tue Wed Thu")
BDay assumes a Monday to Friday week, which is wrong for Saudi Arabia's Friday-Saturday weekend. CustomBusinessDay takes a weekmask of working days and a separate holidays list of actual dates, such as Eid. The UAE moved to a Saturday-Sunday weekend in 2022, so check each country.
49. Integers turn into floats after concat
You concat two DataFrames: one has only an int column a, the other only an int column b. What are the result's dtypes?
- A.Both a and b become float64
- B.Both stay int64
- C.Both become object
- D.a stays int64 and b becomes float64
Show answer
Answer: A. Both a and b become float64
Each frame lacks the other's column, so concat fills those cells with NaN, and NaN forces int64 to float64. IDs then print as 101.0 and can fail to match in later joins. Use nullable "Int64" columns or check dtypes after combining.
50. Which bin holds a boundary value
How many of these values fall in the bin (10, 20]?
import pandas as pd
vals = [5, 10, 15, 20]
pd.cut(vals, bins=[0, 10, 20])
- A.2
- B.3
- C.1
- D.4
Show answer
Answer: A. 2
By default pd.cut uses right=True, so each bin includes its right edge and excludes the left: (0, 10] and (10, 20]. 5 and 10 go in the first bin, and 15 and 20 in the second. With right=False, 10 would move to [10, 20) and 20 would fall outside.
51. Comparing two Series with NaN
a = pd.Series([1.0, np.nan]) and b = pd.Series([1.0, np.nan]). What are (a == b).all() and a.equals(b)?
- A.True and True
- B.False and True
- C.True and False
- D.False and False
Show answer
Answer: B. False and True
With ==, NaN is never equal to anything, including NaN, so the second element compares False and .all() is False. Series.equals treats NaN in the same position as equal and also checks shape and dtype. Use equals, or pandas testing helpers, to compare data in tests.
52. Memory saved by a smaller integer type
A rating column has 10 million values from 1 to 5, stored as int64. About how much memory does converting it to int8 save?
- A.About 10 MB
- B.About 8 MB
- C.About 70 MB
- D.Nothing, since pandas always uses 64 bits
Show answer
Answer: C. About 70 MB
int64 uses 8 bytes per value, so 10 million values take 80 MB. int8 uses 1 byte, about 10 MB, saving about 70 MB. int8 holds −128 to 127, which is plenty for ratings. pd.to_numeric(..., downcast='integer') picks the smallest safe type.
53. Passing a dataframe through XCom
A task pushes a 2 GB pandas DataFrame through Airflow XCom to the next task. What is the better pattern?
- A.Split the DataFrame into 100 separate XCom values
- B.Store the whole DataFrame in an Airflow Variable
- C.Pickle it into the DAG file when the DAG is parsed
- D.Write it to object storage and pass the path in XCom
Show answer
Answer: D. Write it to object storage and pass the path in XCom
XCom lives in the metadata database and is meant for small values such as IDs, counts or file paths. Large data belongs in storage like S3 or GCS; tasks exchange references so the scheduler database stays small and fast.
54. Filtering NaN with == in pandas
df['income'] has 200 missing values. Why does df[df['income'] == np.nan] return an empty DataFrame?
- A.NaN is never equal to anything, even another NaN, so the comparison is always False; use df["income"].isna()
- B.np.nan must be written as None in pandas
- C.pandas stores missing values as 0
- D.The column must be converted to string first
Show answer
Answer: A. NaN is never equal to anything, even another NaN, so the comparison is always False; use df["income"].isna()
By the floating-point standard, NaN == NaN is False, so an equality check can never find missing values. pandas provides isna() and notna() for this. The same idea appears in SQL, where you must use IS NULL instead of = NULL.
55. DAG import timed out
A DAG file imports tensorflow and pandas at the top and builds a 2,000-task DAG. The UI shows "DagBag import timeout". What is the best fix?
- A.Move heavy imports into the task functions
- B.Add retries=5 to every task in the DAG
- C.Increase max_active_runs for the DAG
- D.Switch the executor from Local to Celery
Show answer
Answer: A. Move heavy imports into the task functions
Parsing a DAG file must finish within dagbag_import_timeout (30 seconds by default). Heavy module-level imports and slow generation logic make parsing slow. Import heavy libraries inside the task callables so they load only on the worker at run time.
56. Workers dying on a pandas join
A @task loads two 30 GB tables into pandas on an Airflow worker to join them, and the worker keeps getting OOM-killed. What is the best design change?
- A.Give the worker 4 more GB of memory
- B.Run the join in the warehouse or Spark and orchestrate it from Airflow
- C.Split the task into two tasks that share a global DataFrame
- D.Set retries=10 so it eventually passes
Show answer
Answer: B. Run the join in the warehouse or Spark and orchestrate it from Airflow
Airflow is an orchestrator; workers are sized for coordination, not big data processing. Push the join to a system built for it, such as SQL in the warehouse or a Spark job, and let the task just submit and monitor it.
57. Cached numbers after an overwrite
A notebook cached a DataFrame read from a Parquet path. Another job then overwrote that path with corrected data. The notebook keeps showing the old totals. Why?
- A.Parquet files cannot be overwritten while being read
- B.The cached blocks are reused until you unpersist or refresh
- C.Spark merges the old and new files on every query
- D.The other job wrote its files only to the driver
Show answer
Answer: B. The cached blocks are reused until you unpersist or refresh
Spark serves the cached plan from cached blocks and does not watch the files for changes. Until you call unpersist(), or REFRESH TABLE / spark.catalog.refreshByPath, you see the old data. Uncached reads of changed files can also fail with file-not-found errors until the metadata is refreshed.
58. first() gives different answers
groupBy("customer").agg(F.first("plan")) returns different plans for the same customer on different runs of the same job. Why?
- A.first() picks a random row by design on every single run
- B.The job reads a sample of the data each time
- C.first() returns the last row when AQE is on
- D.Row order after a shuffle is not fixed, so first() varies
Show answer
Answer: D. Row order after a shuffle is not fixed, so first() varies
first() returns whichever row the aggregation sees first, and that depends on partition and arrival order, which can change between runs. Spark marks it non-deterministic. Use max_by("plan", "updated_at") or a row_number window to choose by a clear rule.
59. Changing a streaming aggregation key
You change a streaming query from groupBy("city") to groupBy("city", "channel") and restart with the same checkpoint. It fails. What is the right move?
- A.Delete only the offsets folder inside the checkpoint
- B.Use a new checkpoint, since the old state no longer fits
- C.Restart again; Spark will migrate the old state itself
- D.Add a watermark so the old state is ignored
Show answer
Answer: B. Use a new checkpoint, since the old state no longer fits
The checkpoint holds state shaped by the old aggregation keys, so changing keys, adding stateful operators or changing their order is not compatible. You need a fresh checkpoint and a plan for the gap, such as reprocessing from a chosen offset. Changes like filters or projections on stateless steps are usually allowed.
60. Shuffles after repartition on the same key
How many shuffle (Exchange) steps does the physical plan of this code contain, with AQE off and no prior partitioning?
out = (df.repartition(200, "customer_id")
.groupBy("customer_id")
.agg(F.sum("amount")))- A.0
- B.1
- C.2
- D.3
Show answer
Answer: B. 1
repartition(200, "customer_id") hash-partitions the data by customer_id, which already satisfies the grouping requirement of groupBy("customer_id"). Spark sees this and skips a second Exchange, so there is one shuffle. Grouping by a different column would add another.