Remove rows that contain a missing value from a tabular dataset. By hand, scan parallel lists for None entries and collect only the names whose score is present. With pandas, DataFrame.dropna() filters out every row with at least one NaN in a single call.

By hand

Iterate over range(len(names)). Check scores[i] is not None; when the score is present, append the corresponding name to kept. The trace shows kept growing only on non-missing rows.

naive.py
Replay: real traced execution (multi-file project)
names  = ['Alice', 'Bob', 'Carol', 'Dave', 'Eve']
scores = [82, None, 91, None, 74]
kept = []
for i in range(len(names)):
    if scores[i] is not None:
        kept.append(names[i])
print('RESULT:', kept)
  1. names ← ['Alice', 'Bob', 'Carol', 'Dave', 'Eve']

    1names  = ['Alice', 'Bob', 'Carol', 'Dave', 'Eve']2scores = [82, None, 91, None, 74]
    values this step['Alice', 'Bob', 'Carol', 'Dave', 'Eve']names
  2. scores ← [82, None, 91, None, 74]

    1names  = ['Alice', 'Bob', 'Carol', 'Dave', 'Eve']2scores = [82, None, 91, None, 74]3kept = []
    values this step[82, None, 91, None, 74]scores
  3. kept ← []

    2scores = [82, None, 91, None, 74]3kept = []4for i in range(len(names)):
    values this step[]kept
  4. i ← 0

    3kept = []4for i in range(len(names)):5    if scores[i] is not None:
    values this step0i
  5. if scores[i] is not None:

    4for i in range(len(names)):5    if scores[i] is not None:6        kept.append(names[i])
  6. kept ← ['Alice']

    5    if scores[i] is not None:6        kept.append(names[i])7print('RESULT:', kept)
    values this step[] ['Alice']kept
  7. i ← 1

    3kept = []4for i in range(len(names)):5    if scores[i] is not None:
    values this step0 1i
  8. if scores[i] is not None:

    4for i in range(len(names)):5    if scores[i] is not None:6        kept.append(names[i])
  9. i ← 2

    3kept = []4for i in range(len(names)):5    if scores[i] is not None:
    values this step1 2i
  10. if scores[i] is not None:

    4for i in range(len(names)):5    if scores[i] is not None:6        kept.append(names[i])
  11. kept ← ['Alice', 'Carol']

    5    if scores[i] is not None:6        kept.append(names[i])7print('RESULT:', kept)
    values this step['Alice'] ['Alice', 'Carol']kept
  12. i ← 3

    3kept = []4for i in range(len(names)):5    if scores[i] is not None:
    values this step2 3i
  13. if scores[i] is not None:

    4for i in range(len(names)):5    if scores[i] is not None:6        kept.append(names[i])
  14. i ← 4

    3kept = []4for i in range(len(names)):5    if scores[i] is not None:
    values this step3 4i
  15. if scores[i] is not None:

    4for i in range(len(names)):5    if scores[i] is not None:6        kept.append(names[i])
  16. kept ← ['Alice', 'Carol', 'Eve']

    5    if scores[i] is not None:6        kept.append(names[i])7print('RESULT:', kept)
    values this step['Alice', 'Carol'] ['Alice', 'Carol', 'Eve']kept
  17. for i in range(len(names)):

    3kept = []4for i in range(len(names)):5    if scores[i] is not None:
  18. stdout ← RESULT: ['Alice', 'Carol', 'Eve']

    6        kept.append(names[i])7print('RESULT:', kept)
    values this stepRESULT: ['Alice', 'Carol', 'Eve']stdout

With pandas

Build a DataFrame from the parallel lists — pandas infers float64 for the numeric column, converting None to NaN. df.dropna() returns a new DataFrame without any NaN rows. The snapshot shows the shape before and after so the drop count is immediately visible.

library.py
import pandas as pd
from dalib.display import set_display
set_display()

names  = ['Alice', 'Bob', 'Carol', 'Dave', 'Eve']
scores = [82, None, 91, None, 74]
df = pd.DataFrame({'name': names, 'score': scores})
clean = df.dropna()
result = clean['name'].tolist()
print('columns:', df.columns.tolist())
print('shape before:', df.shape)
print('shape after:', clean.shape)
print('RESULT:', result)
columns: ['name', 'score']
shape before: (5, 2)
shape after: (3, 2)
RESULT: ['Alice', 'Carol', 'Eve']

Implementation notes

  • dropna() drops any row that has at least one NaN by default (how='any'). Pass how='all' to drop only rows where every cell is missing.
  • subset=['score'] limits the check to the score column, leaving rows with missing values in other columns intact.
  • The returned DataFrame keeps its original index labels — use .reset_index (drop=True) after if you need a clean 0-based index.
  • Cross-reference: detect-missing (this chapter) for identifying which positions are missing before deciding whether to drop or fill.