Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.12
1 change: 1 addition & 0 deletions .user.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
id: b7c01a20-043b-414e-9aec-8e45d62cec80
66 changes: 62 additions & 4 deletions AI_ASSIST.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,85 @@ Document one place you used an LLM during this assignment.
Example: "My safe_divide macro compiled but returned NULL for all rows even
when tip_amount and fare_amount were both non-zero." -->

TODO
dbt run --select +fct_daily_borough_stats
17:45:39  Running with dbt=1.11.12
17:45:39  Registered adapter: postgres=1.10.2
17:45:41  Found 1 test, 3 models, 2 sources, 476 macros
17:45:41  
17:45:41  Concurrency: 1 threads (target='dev')
17:45:41  
17:45:43  1 of 3 START sql view model dev_mareh.stg_trips ................................ [RUN]
17:45:45  1 of 3 OK created sql view model dev_mareh.stg_trips ........................... [CREATE VIEW in 1.34s]
17:45:45  2 of 3 START sql view model dev_mareh.stg_zones ................................ [RUN]
17:45:46  2 of 3 OK created sql view model dev_mareh.stg_zones ........................... [CREATE VIEW in 1.22s]
17:45:46  3 of 3 START sql table model dev_mareh.fct_daily_borough_stats ................. [RUN]
17:45:47  3 of 3 ERROR creating sql table model dev_mareh.fct_daily_borough_stats ........ [ERROR in 0.92s]
17:45:48  
17:45:48  Finished running 1 table model, 2 view models in 0 hours 0 minutes and 6.84 seconds (6.84s).
17:45:48  
17:45:48  Completed with 1 error, 0 partial successes, and 0 warnings:
17:45:48  
17:45:48  Failure in model fct_daily_borough_stats (models\marts\fct_daily_borough_stats.sql)
17:45:48    Database Error in model fct_daily_borough_stats (models\marts\fct_daily_borough_stats.sql)
  function avg(text) does not exist
  LINE 44:     avg(t.tip_pct) AS avg_tip_pct,
               ^
  HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
  compiled code at target\run\nyc_taxi_borough_daily\models\marts\fct_daily_borough_stats.sql
17:45:48  
17:45:48    compiled code at target\compiled\nyc_taxi_borough_daily\models\marts\fct_daily_borough_stats.sql
17:45:48  
17:45:48  Done. PASS=2 WARN=0 ERROR=1 SKIP=0 NO-OP=0 TOTAL=3
(c55-data-week-10) 

## The prompt

<!-- TODO: paste the exact prompt you sent to the LLM. -->

TODO
Why i get an error here and is that error have a problem with type of the column?

## The response

<!-- TODO: summarise or paste what the LLM returned. -->

TODO
Yes, your intuition is spot on! The error is 100% related to the data type of the column.
🔍 Breaking Down the Error
The key line in your error log is:

function avg(text) does not exist

In PostgreSQL, the avg() function calculates the mathematical average. Because of this, it expects a numeric data type (like integer, decimal, or numeric).

The error tells us that the tip_pct column is currently being treated as a text (string) data type. PostgreSQL is essentially saying: "I don't know how to calculate a mathematical average of text words or characters."

💡 Why is it text?
When we used the macro {{ safe_divide('tip_amount', 'fare_amount') }} in your stg_trips.sql model, dbt compiled that division in a way that PostgreSQL returned it as a generic or text-like format, instead of a strict numeric type.

🛠️ How to Fix It
Just like the database hint suggested (You might need to add explicit type casts), you need to explicitly tell PostgreSQL to convert (cast) that column into a number before trying to calculate the average.

Go to your fct_daily_borough_stats.sql file, look at line 44, and change this:

SQL
avg(t.tip_pct) AS avg_tip_pct,
To this (adding ::numeric inside the brackets):

SQL
avg(t.tip_pct::numeric) AS avg_tip_pct,
🚀 Try running it again!
Save your file (Ctrl + S) and run your command once more in the terminal:

Bash
dbt run --select +fct_daily_borough_stats
Let me know if it clears up and passes with that beautiful green success message!

## Reflection

<!-- TODO: what did you change, keep, or discard after reviewing the LLM's answer?
Be specific: "I kept the NULLIF suggestion but changed the column alias from
'ratio' to 'tip_pct' to match the assignment schema." -->

TODO
I tried the solution that llm gives me and it's work.

---

Expand Down
Binary file added docs/Lineage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion macros/safe_divide.sql
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
-- TODO: implement the macro body.
-- Use NULLIF(denominator, 0) to avoid division-by-zero errors.
-- Return only the SQL expression (no SELECT, no semicolon).
NULL
{{ numerator }} / NULLIF({{ denominator }}, 0)
{% endmacro %}
6 changes: 6 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
def main():
print("Hello from c55-data-week-10!")


if __name__ == "__main__":
main()
25 changes: 16 additions & 9 deletions models/marts/_fct_daily_borough_stats.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,29 @@ version: 2
models:
- name: fct_daily_borough_stats
description: >
TODO: state the grain (one row per ___), the source lineage
(built from ___ and ___), and at least one known caveat
(e.g. rows dropped in staging, any WARN-severity tests).
The grain of this mart is one row per (pickup_borough, pickup_date).
It is built from stg_trips and stg_zones.
Note: Trips with invalid or unmatched zone IDs (such as location 999)
were dropped during the staging/join process using an INNER JOIN,
ensuring pickup_borough is never NULL.
# TODO: Task 5 -- add the compound uniqueness test on the mart's primary
# key (pickup_borough, pickup_date). You need the dbt_utils package for
# this: declare it in packages.yml and run `dbt deps` first.
tests:
- dbt_utils.unique_combination_of_columns:
combination_of_columns:
- pickup_borough
- pickup_date
columns:
- name: pickup_borough
description: "TODO: explain what this column contains and where it comes from"
description: "It is the name of the NYC borough where the trip started, It comes from the stg_zones table."
- name: pickup_date
description: "TODO: explain units and how it is derived"
description: "It is the date of the trip, derived from the pickup_datetime column in stg_trips which is converted to date."
- name: trip_count
description: "TODO: explain what is counted (unit: number of trips)"
description: "It is the total number of trips per borough per day."
- name: total_fare
description: "TODO: explain units (USD) and what fare_amount represents"
description: "It is the sum of all fare_amount values for trips per borough per day in USD."
- name: avg_tip_pct
description: "TODO: explain the ratio (tip_amount / fare_amount)"
description: "It is the average tip percentage,calculated as (tip_amount / fare_amount) * 100."
- name: avg_trip_distance
description: "TODO: explain units (miles, from TLC source data)"
description: "The average distance traveled per trip, measured in miles from the source TLC data."
16 changes: 8 additions & 8 deletions models/marts/fct_daily_borough_stats.sql
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ SELECT
-- total_fare NUMERIC - sum(fare_amount)
-- avg_tip_pct NUMERIC - avg(tip_pct)
-- avg_trip_distance NUMERIC - avg(trip_distance)
NULL AS pickup_borough,
NULL AS pickup_date,
NULL AS trip_count,
NULL AS total_fare,
NULL AS avg_tip_pct,
NULL AS avg_trip_distance
z.borough AS pickup_borough,
t.pickup_datetime::date AS pickup_date,
count(*) AS trip_count,
sum(t.fare_amount) AS total_fare,
avg(t.tip_pct::numeric) AS avg_tip_pct,
avg(t.trip_distance) AS avg_trip_distance

FROM trips t
-- TODO: add JOIN to zones here
-- TODO: add GROUP BY here
inner join zones z on t.pickup_location_id = z.location_id
GROUP BY pickup_borough, pickup_date
6 changes: 3 additions & 3 deletions models/staging/_sources.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ version: 2

sources:
- name: nyc_taxi
schema: nyc_taxi # TODO: confirm this matches the schema where raw_trips and raw_zones live
schema: nyc_taxi
tables:
- name: raw_trips
description: "TODO: one sentence on what this table contains and its grain"
description: "Contains raw NYC taxi trip records, including timestamps, distances, fare amounts, and location IDs. The grain of this table is one row per individual taxi trip."
- name: raw_zones
description: "TODO: one sentence on what this table contains"
description: "A lookup table that maps each unique location ID to its corresponding borough, zone name, and service zone."
20 changes: 11 additions & 9 deletions models/staging/_stg_trips.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@ version: 2

models:
- name: stg_trips
description: "TODO: state the grain (one row per ___) and what source this reads from"
description: "One row per individual taxi trip is the grain of this model."
columns:
- name: pickup_datetime
description: "TODO"
# TODO: Task 5 -- add not_null tests on every column used as a join or
# group-by key. See the chapter's dbt Tests section for the syntax.
description: "It count the date and time when the trip started."
tests:
- not_null
- name: pickup_location_id
description: "TODO"
description: "Its the location id of the pickup where the trip started."
tests:
- not_null
- name: fare_amount
description: "TODO"
description: "It is the amount of money paid for the trip in USD. "
- name: tip_amount
description: "TODO"
description: "It is the amount of money paid as a tip for the trip in USD."
- name: trip_distance
description: "TODO"
description: "It is the distance of the trip in miles."
- name: tip_pct
description: "TODO"
description: "It is percentage of the tip amount over the fare amount."
9 changes: 6 additions & 3 deletions models/staging/_stg_zones.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ version: 2

models:
- name: stg_zones
description: "TODO: state the grain and what source this reads from"
description: "One row per individual taxi zone is the grain of this model."
columns:
- name: location_id
description: "TODO"
description: "It is the unique identifier for each taxi zone."
# TODO: Task 5 -- this column is the join key. Which two generic tests
# guarantee a clean one-to-many join from stg_trips?
tests:
- not_null
- unique
- name: borough
description: "TODO"
description: "It is the NYC borough where the taxi zone is located."
14 changes: 7 additions & 7 deletions models/staging/stg_trips.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
-- Downstream: fct_daily_borough_stats joins this to stg_zones.

SELECT
-- TODO: select the columns you need for the mart:
-- pickup_datetime, pickup_location_id, fare_amount, tip_amount, trip_distance
--
-- TODO: add tip_pct using {{ safe_divide('tip_amount', 'fare_amount') }}
--
-- TODO: filter out rows where pickup_location_id IS NULL or fare_amount < 0

pickup_datetime,
pickup_location_id,
fare_amount,
tip_amount,
trip_distance,
{{ safe_divide('tip_amount', 'fare_amount') }} AS tip_pct
FROM {{ source('nyc_taxi', 'raw_trips') }}
Where pickup_location_id IS NOT NULL AND fare_amount >= 0
4 changes: 2 additions & 2 deletions models/staging/stg_zones.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
-- Exposes location_id and borough for use as a lookup in the mart.

SELECT
-- TODO: select location_id and borough from {{ source('nyc_taxi', 'raw_zones') }}

location_id,
borough
FROM {{ source('nyc_taxi', 'raw_zones') }}
5 changes: 5 additions & 0 deletions package-lock.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
packages:
- name: dbt_utils
package: dbt-labs/dbt_utils
version: 1.4.1
sha1_hash: e6424ba9e5a22487e47f023803aa4f0411946808
5 changes: 4 additions & 1 deletion packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@
# `dbt deps` to install it. You need it for the compound uniqueness test
# on the mart. See https://hub.getdbt.com/dbt-labs/dbt_utils/latest/
# for the package block syntax.
packages: []
packages:
- package: dbt-labs/dbt_utils
#version: 1.4.1
version: [">=1.1.0", "<2.0.0"]
9 changes: 9 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[project]
name = "c55-data-week-10"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"dbt-postgres>=1.10.2",
]
56 changes: 43 additions & 13 deletions reports/answers.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@ Queries run against `dev_<your_name>.fct_daily_borough_stats`.
**SQL:**

```sql
-- TODO: query fct_daily_borough_stats grouped by pickup_borough, sum total_fare, order DESC
SELECT
pickup_borough,
SUM(total_fare) AS grand_total_fare
FROM dev_mareh.fct_daily_borough_stats
GROUP BY pickup_borough
ORDER BY grand_total_fare DESC;
```

**Result:** TODO

**Interpretation:** TODO (one sentence)
**Result:**
Manhattan: 493,955.62
Queens: 274,214.90
**Interpretation:** Manhattan has the highest total fare across the entire dataset, generating nearly double the revenue of the second-highest borough, Queens.

---

Expand All @@ -21,12 +27,18 @@ Queries run against `dev_<your_name>.fct_daily_borough_stats`.
**SQL:**

```sql
-- TODO: query fct_daily_borough_stats grouped by pickup_date, sum trip_count, order DESC LIMIT 1
SELECT
pickup_date,
SUM(trip_count) AS daily_trips
FROM dev_mareh.fct_daily_borough_stats
GROUP BY pickup_date
ORDER BY daily_trips DESC
LIMIT 1;
```

**Result:** TODO
**Result:** pickup_date:2024-01-17 daily_trips: 2,221

**Interpretation:** TODO (one sentence)
**Interpretation:** January 17, 2024, was the busiest day in the dataset, recording the highest overall trip volume with 2,221 trips.

---

Expand All @@ -35,12 +47,23 @@ Queries run against `dev_<your_name>.fct_daily_borough_stats`.
**SQL:**

```sql
-- TODO: query fct_daily_borough_stats order by avg_tip_pct DESC LIMIT 5
SELECT
pickup_borough,
pickup_date,
avg_tip_pct
FROM dev_mareh.fct_daily_borough_stats
ORDER BY avg_tip_pct DESC
LIMIT 5;
```

**Result:** TODO
**Result:**
Unknown (2024-01-30): 2.500125
Unknown (2024-01-07): 1.339630
Unknown (2024-01-11): 1.104496
Unknown (2024-01-16): 1.000000
Unknown (2024-01-18): 0.611111

**Interpretation:** TODO — note whether any avg_tip_pct > 1 rows appear and what causes them
**Interpretation:** Yes, there are rows where avg_tip_pct > 1 (specifically on Jan 30, Jan 7, and Jan 11, all in the "Unknown" borough). This occurs because the "Unknown" borough represents a very small-sample bucket where a few outlier trips with exceptionally high tips easily skew and dominate the daily average.

---

Expand All @@ -49,9 +72,16 @@ Queries run against `dev_<your_name>.fct_daily_borough_stats`.
**SQL:**

```sql
-- TODO: use percentile_cont(0.5) WITHIN GROUP (ORDER BY trip_count) filtered by borough
SELECT
pickup_borough,
percentile_cont(0.5) WITHIN GROUP (ORDER BY trip_count) AS median_trip_count
FROM dev_mareh.fct_daily_borough_stats
WHERE pickup_borough IN ('Manhattan', 'Brooklyn')
GROUP BY pickup_borough;
```

**Result:** TODO
**Result:**
Brooklyn: 248
Manhattan: 1,169.5

**Interpretation:** TODO (one sentence on the ratio)
**Interpretation:** Manhattan has a significantly higher median daily trip count compared to Brooklyn, outperforming it by nearly 4.7 times.
9 changes: 7 additions & 2 deletions tests/assert_avg_tip_pct_within_bounds.sql
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,10 @@

-- TODO: write the SELECT here.
-- Query {{ ref('fct_daily_borough_stats') }} and return rows where avg_tip_pct > 1.
SELECT NULL AS pickup_borough, NULL AS pickup_date, NULL AS avg_tip_pct
WHERE FALSE -- TODO: replace with the real query
{{ config(severity='warn') }}
SELECT
pickup_borough,
pickup_date,
avg_tip_pct
FROM {{ ref('fct_daily_borough_stats') }}
WHERE avg_tip_pct > 1
Loading
Loading