Module 20 · Core

Read and write files

Move data between pandas and common local file formats.

Before you start: Know DataFrame construction, column selection, and paths.

Python loads from this site on your first run.

Each run starts with fresh data. Download any exported files before another run. Your written answers and code drafts are saved on this device; export your work to keep a backup.

M20C01 · Explore · predict · practice

Read and export CSV

CSV stores text rows separated by delimiters. read_csv creates a DataFrame and attempts to infer column types. to_csv writes the selected data; index=False avoids saving the pandas row index as an extra column.

Read the example

df = pd.read_csv(DATA_DIR / "students.csv", dtype={"Student_ID": "string"})
result = df.loc[df["Score"].ge(85), ["Student_ID", "Name", "Score"]]
result.to_csv(OUTPUT_DIR / "high_scores.csv", index=False)
print(result)
print("Saved outputs/high_scores.csv")
Verified example output · pandas 2.2.3
  Student_ID    Name  Score
0       S001   Amira     88
2       S003   Chloe     91
5       S006  Fatima     95
9       S010   Jamal     89
Saved outputs/high_scores.csv

Watch for this. CSV does not preserve every pandas dtype. Check types and missing-value rules when reloading.

Your turn

Read only Student_ID, Name, and Attendance. Save students with Attendance below 85 to attendance_followup.csv.

For the code check, keep these result variable names: result. Different valid approaches are welcome.

Show a hint

Use usecols when reading and .lt(85) when filtering.

Show one worked solution
df = pd.read_csv(DATA_DIR / "students.csv", usecols=["Student_ID", "Name", "Attendance"], dtype={"Student_ID": "string"})
result = df.loc[df["Attendance"].lt(85)]
result.to_csv(OUTPUT_DIR / "attendance_followup.csv", index=False)
print(result)

The report contains S004, S008, and S011. All written files go in outputs.

Compare your explanations
  1. Read. A platform-independent path to a file inside the data directory.
  2. Predict. 4 students: S001, S003, S006, and S010.
  3. Explain. IDs are labels, not quantities; text preserves formats such as leading zeros.

One quick check

Why use index=False on export?

What you should be able to explain. Inspect imports and choose export columns deliberately.

M20C02 · Explore · predict · practice

Move records through JSON

JSON can store a list of records, each represented by a dictionary. orient="records" makes this structure explicit. Re-reading a file checks that the exported shape matches what you intended.

Read the example

df = pd.read_json(DATA_DIR / "students.json", orient="records")
result = df[["Student_ID", "Campus", "Score"]].head(3)
result.to_json(OUTPUT_DIR / "student_preview.json", orient="records", indent=2)
print(result)
print((OUTPUT_DIR / "student_preview.json").read_text())
Verified example output · pandas 2.2.3
  Student_ID Campus  Score
0       S001    RAK     88
1       S002  Dubai     72
2       S003    RAK     91
[
  {
    "Student_ID":"S001",
    "Campus":"RAK",
    "Score":88
  },
  {
    "Student_ID":"S002",
    "Campus":"Dubai",
    "Score":72
  },
  {
    "Student_ID":"S003",
    "Campus":"RAK",
    "Score":91
  }
]

Watch for this. File formats can change date and nullable dtype representations; a round trip needs validation.

Your turn

Export Student_ID and Program for the first two students, then read the saved JSON back into result.

For the code check, keep these result variable names: result. Different valid approaches are welcome.

Show a hint

Write with orient="records" and read with the same orientation.

Show one worked solution
df = students().head(2)[["Student_ID", "Program"]]
path = OUTPUT_DIR / "program_preview.json"
df.to_json(path, orient="records", indent=2)
result = pd.read_json(path, orient="records")
print(result)

The round trip preserves these two records and their field names.

Compare your explanations
  1. Read. One student row, with column names as dictionary keys.
  2. Predict. 3, because head(3) selects three rows.
  3. Explain. It shows the actual file structure, not only its DataFrame representation.

One quick check

What does orient="records" produce?

What you should be able to explain. Choose a file orientation that matches the receiving system.

M20C03 · Explore · predict · practice

Select an Excel worksheet

An Excel workbook can contain several sheets. sheet_name selects one by name. The openpyxl engine handles the provided .xlsx workbook and is listed in the installation requirements.

Read the example

df = pd.read_excel(DATA_DIR / "students.xlsx", sheet_name="Students", engine="openpyxl")
result = df.groupby("Campus", as_index=False)["Score"].mean()
result.to_excel(OUTPUT_DIR / "campus_report.xlsx", sheet_name="Summary", index=False, engine="openpyxl")
print(result.round(2))
Verified example output · pandas 2.2.3
  Campus  Score
0  Dubai  76.83
1    RAK  81.00

Watch for this. If openpyxl is missing, install the provided requirements before running this cell.

Your turn

Read the Students sheet. Store a summary in result with columns Program and Students, where Students counts rows per program. Export it to program_counts.xlsx, sheet Counts.

For the code check, keep these result variable names: result. Different valid approaches are welcome.

Show a hint

groupby("Program").size() counts rows; name the count column.

Show one worked solution
df = pd.read_excel(DATA_DIR / "students.xlsx", sheet_name="Students", engine="openpyxl")
result = df.groupby("Program").size().rename("Students").reset_index()
result.to_excel(OUTPUT_DIR / "program_counts.xlsx", sheet_name="Counts", index=False, engine="openpyxl")
print(result)

The workbook contains two counts: six AI students and six IT students.

Compare your explanations
  1. Read. students.xlsx is the workbook file; Students identifies a worksheet inside it.
  2. Predict. 2, one for Dubai and one for RAK.
  3. Explain. It preserves the supplied source data and clearly identifies the generated report.

One quick check

Which argument chooses a worksheet?

What you should be able to explain. Excel I/O needs a workbook path and the correct worksheet.

M20C04 · Explore · predict · practice

Query a small SQLite table

SQL retrieves data from tables using a query. This example creates a temporary in-memory database from the fictional data. The parameter placeholder passes a value separately from the query text.

Read the example

with sqlite3.connect(":memory:") as connection:
    students().to_sql("students", connection, index=False, if_exists="replace")
    result = pd.read_sql_query(
        "SELECT Student_ID, Name, Score FROM students WHERE Score >= ? ORDER BY Score DESC",
        connection, params=(85,))
print(result)
Verified example output · pandas 2.2.3
  Student_ID    Name  Score
0       S006  Fatima     95
1       S003   Chloe     91
2       S010   Jamal     89
3       S001   Amira     88

Watch for this. Only use if_exists="replace" here on the temporary classroom table; on a real database it replaces the existing table.

Your turn

Use SQL to calculate mean Score and student count for each Campus. Store the returned DataFrame in result with columns Campus, Mean_Score, and Students, ordered by Campus.

For the code check, keep these result variable names: result. Different valid approaches are welcome.

Show a hint

Use GROUP BY Campus with AVG(Score) AS Mean_Score and COUNT(*) AS Students.

Show one worked solution
with sqlite3.connect(":memory:") as connection:
    students().to_sql("students", connection, index=False, if_exists="replace")
    result = pd.read_sql_query(
        "SELECT Campus, AVG(Score) AS Mean_Score, COUNT(*) AS Students FROM students GROUP BY Campus ORDER BY Campus",
        connection)
print(result.round(2))

This performs the grouping inside SQL and returns the two-row result as a DataFrame.

Compare your explanations
  1. Read. The database exists in memory for this connection and is not a disk database file.
  2. Predict. Fatima, S006, with Score 95.
  3. Explain. It passes values safely without manually inserting them into SQL text.

One quick check

What does read_sql_query return?

What you should be able to explain. Pandas can receive SQL results without loading every source row.

Further detail: official documentation for this topic.