PrerequisiteNo programming background required; AI 101 is recommended but not required
0 / 5 completed
Sync my progressJoin a session to keep progress across devices and share it with your instructor
Learning objectives
Turn a vague ESG question into one that data can answer, naming its Question, Measure, Dimension, and Evidence boundary
Explain row, column, observation, variable, dimension, measure, metric, and unit, and tell absolute metrics apart from intensity metrics
Run basic Python and pandas code in a notebook in the right order, and read the error messages it produces
Use pandas to inspect, select, filter, and sort data, checking the result after every step
Complete a first Guided Lab: calculate Scope 1+2 emissions and emissions intensity, and sort observations into fact, interpretation, and needs verification
Questions and data
30 min
From ESG Questions to Data Questions
Learning objective: define what you want to answer first, then decide which data and which method you need.
Data Science begins with a question, not with Python. If the question is vague, even well-written code only produces numbers without a direction. So the first step in this course is to rewrite a broad concern as a specific question that data can actually answer.
A vague question
Is our ESG performance good? (Good by what definition? Compared with whom? In which year? Using which numbers? None of this is stated.)
Questions data can answer
Change
How did Scope 1 + 2 emissions change from 2021 to 2025?
Comparison
Which year had the highest emissions intensity?
Alternative explanation
Did emissions fall because efficiency improved, or simply because revenue fell?
Data quality
Which companies or years require further data-quality review?
Four components that turn a question into a data question
Component
Ask yourself
Example
Question
What exactly do I want to answer?
How did Scope 1 + 2 emissions change from 2021 to 2025?
Measure
Which value will I calculate or compare, and in what unit?
scope1_tco2e + scope2_tco2e, in tCO2e
Dimension
Will I compare by company, by year, or by another category?
Look at company_name and year separately
Evidence boundary
How far can the available data support an answer, and what can it not tell me?
The data can describe how emissions changed, but cannot on its own prove why
The DS 101 learning loop
Question: write down what you want to answer
Data: identify the columns, units, and scope you need
Python: use the minimum code necessary to work with the data
Analysis: calculate metrics and compare them
Verification: check formulas, units, outputs, and claims
Communication: state the evidence and its limitations
Warning
Data safety: do not paste confidential institutional or company data, client data, or any personal data into an AI tool without authorization. Every exercise in this course uses the synthetic course files. If you later want to analyze real ESG or internal operational data, check your organization's data policy first.
Info
Start with the question, not the code. AI can help you break a question down, but you decide what is being asked, and you review and remain responsible for the conclusion.
Does it name a time range? "Recently" should become a specific span, such as 2021 to 2025.
Does it name a comparison? Against your own past results, or against the other sample companies?
Does it state the unit clearly? "Emissions" should say whether it means tCO2e or an intensity metric.
Does it state the boundary of the data? For example, this dataset only runs through 2025 and cannot say what happens after that.
Worked example: narrowing a question that is still too broad
A manager asks, "How is Metro Services Co. doing?" This cannot be answered directly with data: it has no comparison, time range, or metric. Narrowed down: Question (From 2023 to 2025, is Metro Services Co.'s Scope 1+2 emissions intensity higher or lower than the other three sample companies?), Measure (scope1_tco2e + scope2_tco2e, divided by revenue_m_twd), Dimension (compare by company_name and year), Evidence boundary (the data can describe relative levels, but cannot explain what caused any difference).
Check yourself
Which question is best suited to being answered directly with structured ESG data?
Key takeaway: Define the question before writing code. Analysis quality begins with problem framing.
30 min
Understanding ESG Datasets and Metrics
Learning objective: understand row, column, observation, variable, dimension, measure, metric, and unit.
This course uses DS101_ESG_Company_Data_Clean.csv. It is synthetic educational data: four sample companies (Alpha Tech Co., Harbor Foods Co., Evergreen Components Co., and Metro Services Co.), each with one row per year from 2020 to 2025, for 24 rows in total. The company names and values were designed for teaching and do not represent any real company.
Illustration of what the data looks like (values written for explanation only, not taken from the course file)
company_id
company_name
sector
year
revenue_m_twd
scope1_tco2e
scope2_tco2e
C001
Alpha Tech Co.
Technology
2024
5000
1200
3400
C001
Alpha Tech Co.
Technology
2025
5200
1100
3300
Basic vocabulary for reading a Dataset
Term
Meaning
In the course data
Row / Observation
One record, describing one observed unit
One company in one year
Column / Variable
One field that records the same characteristic for every row
For example year or energy_mwh
Dimension
A field used to describe, group, or compare
company_name, year, sector
Measure
A numeric value you can sum, compare, or calculate with
scope1_tco2e, energy_mwh, revenue_m_twd
Metric
An interpretable number defined or calculated from the data
Scope 1+2 emissions, emissions intensity
Unit
How a value is measured; always read it together with the value
tCO2e, MWh, NT$ million
Scope 1, 2, and 3: this course uses conceptual definitions only
Scope
Concept
How this course uses it
Scope 1
Direct emissions from the organization, such as fuel burned in its own equipment
Uses scope1_tco2e
Scope 2
Indirect emissions from purchased energy such as electricity
Uses scope2_tco2e
Scope 3
Other indirect emissions across the value chain, such as suppliers and product use
The data include a scope3_tco2e column, but this foundation course does not do full Scope 3 accounting and leaves it out of the core formula
Absolute metric
Looks at the total, such as Scope 1+2 emissions in tCO2e
Answers "how much was emitted in total?"
Moves with company size and level of activity
Intensity metric
Divides the total by a base, such as emissions per NT$ million revenue
Answers "how much was emitted relative to the scale of operations?"
Changes when the denominator changes, so read the numerator and denominator together
Formulas used in this course (simplified educational examples)
Scope 1+2 emissions = scope1_tco2e + scope2_tco2e
Emissions intensity = Scope 1+2 emissions / revenue_m_twd
Unit = tCO2e per NT$ million revenue
Warning
A metric that is calculated correctly is not automatically comparable across companies. You still need to check organizational boundaries, accounting methods, units, and data completeness. The formulas in this course are simplified educational examples and are not equivalent to any specific reporting standard or official accounting method.
Info
A number without a definition and unit is not evidence. When you see "1000", ask first: 1000 of what? tCO2e, MWh, or NT$ million? For which company, and which year?
Worked example: the same numbers, two metrics
Continuing the illustration table above (for explanation only, not the actual course file values): Alpha Tech Co.'s Scope 1+2 emissions went from 1200+3400=4600 tCO2e in 2024 to 1100+3300=4400 tCO2e in 2025, so absolute emissions fell. But revenue for the same years rose from 5000 to 5200 (NT$ million), so emissions intensity moved from 4600/5000=0.92 down to 4400/5200≈0.85 (tCO2e per NT$ million revenue). Both metrics fall in the same direction, but for possibly different reasons: one looks only at the total, the other also accounts for the scale of revenue.
When the concern is total emissions responsibility, regulatory reporting, or a net-zero target, absolute emissions is usually the primary figure.
When comparing companies of different sizes, or the same company across years of changing operations, you usually need the intensity metric too.
Calling a drop in absolute emissions "progress" on its own can overlook a simultaneous drop in revenue.
Calling a drop in intensity "progress" on its own can overlook absolute emissions that are still rising.
Check yourself
A sample company's Scope 1+2 emissions fell, but its revenue also fell sharply over the same period. What is the most appropriate approach?
Key takeaway: A number is only meaningful together with its definition and unit.
Notebook and pandas
35 min
Notebook and Essential Python
Learning objective: run basic Python and pandas code in a notebook, without needing to memorize syntax.
A mental model of a notebook
Element
Purpose
Markdown cell
Text that records your question, assumptions, explanations, and conclusions, so others can see why you did what you did
Code cell
Python code that runs and carries out the analysis
Output
What the code produces when it runs: a table, a number, or an error message
Run order
The notebook remembers what has already run, so the order in which cells run affects the result
Info
Run order matters. A reproducible notebook runs from top to bottom in one pass from a fresh kernel (for example with Restart & Run All). If it only works when cells are clicked in a special order, nobody else can reproduce your result.
Minimal Python: assigning values
# Use = to store a value in a variable (assignment)
company = "Alpha Tech" # text (a string) goes in quotes
year = 2025 # numbers do not
threshold = 1000
Assignment: = stores a value under a name you can use later
Strings and numbers: text goes in quotes, such as "Technology"; numbers do not, such as 2025
Function calls: a name followed by parentheses, such as pd.read_csv(...)
Dot notation: it is enough to recognize df.head() as "do head on df"
Comments: text after # is not run; use it to explain what a line does
Reading error messages: start from the last line, which usually names the exact variable or column that caused the problem
Minimal pandas: load the data and look at the first rows
import pandas as pd
# The file must be in the same folder as the notebook
df = pd.read_csv("DS101_ESG_Company_Data_Clean.csv")
df.head()
Reading an error message
You type df["scope_1"] and the last line says KeyError: 'scope_1'. Nothing is broken: the message is telling you there is no column called scope_1. The next step is to run df.columns and look at the real column names, where you will find scope1_tco2e. An error message is a clue, not a failure.
1Explain line by line Ask AI to explain what each line of code does, not just to summarize the whole block.
2List the columns used Ask AI which columns the code uses, then compare them with df.columns or the data dictionary.
3State the expected output Ask AI how many rows and columns, or what kind of result, the code should produce, then check it against the real output.
4No hallucinated columns Tell AI to use only columns that exist in the data. If you see a column name you do not recognize, check the data dictionary before using it.
Warning
When you use an AI tool, share only the synthetic course data or column names. Do not upload confidential institutional or company data without authorization, and do not put personal or sensitive data into public or shared prompts. An AI explanation can help you understand, but it is not evidence in itself: code that runs can still be wrong.
Common error messages at a glance (read the last line first)
Error
What it usually means
What to check first
NameError
A name is used before it exists
Whether the cell that creates that variable has already run
KeyError
A column name does not exist in the data
The real spelling and capitalization in df.columns
SyntaxError
A bracket, quote, or indentation is incomplete
Whether a bracket or quote is missing, usually near the reported line
TypeError
An operation was applied to incompatible types, such as adding text and a number
Whether that column's data type (df.dtypes) is what you expected
Have all the names this cell uses already been created in an earlier cell?
If you reopened an older notebook, did you run Restart & Run All before trusting the output?
When you paste someone else's code, do the column names match your own data, rather than the columns in their example?
Check yourself
You open a notebook and immediately run a cell containing df.head(). It shows NameError: name 'df' is not defined. What is the best response?
Key takeaway: You are not being tested on memorizing Python; you are learning to understand and verify an analysis workflow.
40 min
Inspect, Select, and Filter with pandas
Learning objective: use pandas to answer "what does the data look like, which columns does it have, and which rows meet a condition?"
Three common slips: testing equality needs two equals signs, == (a single = assigns a value); combine conditions with & and wrap each condition in parentheses; text must match exactly, so "technology" and "Technology" are different values in pandas.
Sort: Scope 1 emissions from highest to lowest
df.sort_values("scope1_tco2e", ascending=False)
Row count: how many rows are left, and is that what you expected?
Selected columns: are these the columns you meant, with nothing missing or extra?
Filter condition: does the condition say what you meant (year, sector, greater than or greater than or equal to)?
Unexpected empty result: if you get 0 rows, check spelling, capitalization, and data types before concluding that "there is no data"
Surprising maximum or minimum: confirm the unit and definition first, then decide whether it needs verification
Three things to state for every step
What went in: for example the full df, 24 rows
What was done: for example keep only rows where year is 2025
What remains: for example 4 rows, one per company
Warning
Code that runs can still be wrong. df[df["sector"] == "technology"] raises no error, but it returns 0 rows because the value in the data is "Technology". If you do not check the row count, you might conclude that the sector has no data.
df["sector"].unique(): lists the distinct values in a column, useful for spotting inconsistent spelling
df["sector"].value_counts(): counts how often each value appears, good for a quick look at the category breakdown
df["sector"].nunique(): the number of distinct values, when you do not need to see every one of them
df.isna().sum(): how many missing values each column has; check this before filtering so you do not undercount rows
Worked example: what went in, what was done, what remains
What went in: the full df, 24 rows. What was done: df[df["company_name"] == "Harbor Foods Co."].sort_values("year"), which keeps only Harbor Foods Co. rows and sorts them by year. What remains: you would expect 6 rows (one per year from 2020 to 2025), with year in ascending order; if the row count is not 6, the company name may be spelled inconsistently, so check with df["company_name"].unique() first.
Check yourself
Which pandas expression filters the rows where year is 2025?
Key takeaway: Every analysis step should state what went in, what was done, and what remains.
Guided Lab
45 min
Guided Lab: Your First ESG Data Analysis
Learning objective: with teacher guidance, complete a small reproducible analysis of the clean dataset and sort your observations into fact, interpretation, and needs verification.
Info
Lab question: From 2021 to 2025, how did Scope 1 + 2 emissions and emissions intensity change across the four sample companies, and which observations require further verification?
Steps 6 to 8: filter, sort, and organize annual values by company
# Step 6: keep only 2021 to 2025
df_lab = df[(df["year"] >= 2021) & (df["year"] <= 2025)]
df_lab.shape # expect 20 rows: 4 companies x 5 years
# Step 7: pick one year and sort by emissions intensity
df_lab[df_lab["year"] == 2025].sort_values("scope12_intensity", ascending=False)[["company_name", "year", "scope12_intensity"]]
# Step 8: group by company and year, keeping the year dimension
annual = (
df_lab.groupby(["company_name", "year"])[["scope12_tco2e", "revenue_m_twd"]]
.sum()
.reset_index()
)
# Recalculate intensity from the summed numerator and denominator; never sum intensities
annual["scope12_intensity"] = annual["scope12_tco2e"] / annual["revenue_m_twd"]
annual
Label each observation by type of evidence (sentence patterns; fill in your own output)
Label
Meaning
Sentence pattern
Fact from data
A number read directly from the output that someone else can reproduce
In this synthetic dataset, [company]'s scope12_tco2e went from [value] in 2021 to [value] in 2025 (tCO2e).
Interpretation
Your reading of the facts, with its basis and uncertainty stated
[Company]'s emissions intensity fell more than its absolute emissions, which may partly reflect higher revenue rather than necessarily better emissions efficiency.
Needs verification
Something the data cannot confirm, which needs other sources or a question to the data provider
[Company]'s change in [year] is unusually large; check whether the organizational boundary or calculation method changed that year.
A notebook that runs again from top to bottom
Two derived columns: scope12_tco2e and scope12_intensity
At least three clearly labelled evidence statements (fact, interpretation, or needs verification)
At least one limitation statement
No causal claim that the dataset cannot support
Warning
This data can describe what changed, but it cannot on its own show why. Avoid causal claims such as "emissions fell because the company adopted renewable energy", and do not treat the results as a company ranking or investment advice. Example limitation: "This analysis uses synthetic teaching data and a simplified formula, and it does not check whether organizational boundaries and calculation methods are consistent across companies, so cross-company comparisons are for practice only."
Done
If you use AI to write or explain this code, check each point yourself: the column names exist, the formulas match the course definitions, the units are right, and the row counts are what you expect. AI can assist the analysis; responsibility for verification and the conclusion stays with you.
Common slips in this lab (use it to self-check)
Slip
Why it causes an error
How to avoid it
Summing or averaging scope12_intensity directly after grouping
Intensity is a ratio; summing or averaging it directly distorts the denominator
Sum the numerator and denominator separately first, then divide again to get a correct intensity
Ranking every company from a single year alone
One year can be skewed by a one-off event and may not represent a longer trend
Look across the full 2021-2025 range before describing a trend
Writing a falling intensity directly as "the company improved"
A falling intensity can simply mean revenue grew, enlarging the denominator
Cite the absolute emissions figure too, and state in your limitations that the cause cannot be confirmed
Key takeaway: A good analysis produces not only numbers but also keeps the formula, units, evidence, and limitations.