

0 / 2 embers
0 / 3000 xp
click for more info
Complete a lesson to start your streak
click for more info
Difficulty: 4
click for more info
Not enough gems
Cost: 6 gems
1: Aggregation
incomplete
2: Grouping With Dictionaries
incomplete
3: Grouping in Pandas
incomplete
4: Category Type
incomplete
5: Grouping by Multiple Columns
incomplete
6: Multiple Aggregations
incomplete
7: Named Aggregations
incomplete
8: Custom Aggregations
incomplete
9: Pivot Tables
incomplete
10: Pivot Table Aggregations
incomplete
11: Star Schema
incomplete
12: Grain Validation
incomplete
13: Fixing Grain Violations
incomplete
14: Building a Data Model
incomplete
Back
ctrl+,
Next
ctrl+.
This lesson's interactive features are locked, please to keep using them
The grain of a table is what one row represents. An orders table might have one row per order, or one row per line item. Getting this wrong leads to double-counting and incorrect numbers in every report that uses the table.
If your orders table has a grain of "one row per order" but accidentally contains duplicate order IDs, every aggregation will be inflated:
# This looks fine...
orders = pd.DataFrame(
{
"order_id": ["ORD-7K2M", "ORD-9P4X", "ORD-9P4X", "ORD-3D8V"],
"revenue": [50, 75, 75, 100],
}
)
# But total revenue is wrong
print(orders["revenue"].sum()) # 300, but it should be 225
Order ORD-9P4X appears twice, and revenue is overcounted by $75. This is a grain violation.
Use .duplicated() to check whether your grain is valid:
# Check for duplicate order IDs
duplicates = orders[orders.duplicated(subset=["order_id"], keep=False)]
print(f"Found {len(duplicates)} rows with duplicate order_ids")
If you get zero duplicates, your grain is valid.
Sometimes multiple columns define the grain. An order line item table might have a grain of "one row per order + product combination":
line_items = pd.DataFrame(
{
"order_id": ["ORD-7K2M", "ORD-7K2M", "ORD-9P4X", "ORD-9P4X"],
"product_id": ["CHL-482", "TST-731", "CHL-482", "OVN-205"],
"quantity": [2, 1, 3, 1],
}
)
# Validate: no duplicate order_id + product_id combinations
dupes = line_items.duplicated(subset=["order_id", "product_id"])
print(f"Grain violations: {dupes.sum()}") # 0, so the grain is valid