{
 "id": "orders_stuck",
 "title": "Where do orders get stuck?",
 "question": "Where do late orders lose their time: before approval, before the carrier handoff, or after it?",
 "source": {
  "name": "Brazilian E-Commerce Public Dataset by Olist",
  "publisher": "Olist",
  "url": "https://www.kaggle.com/datasets/olistbr/brazilian-ecommerce",
  "license": "CC BY-NC-SA 4.0",
  "as_of": "August 2018",
  "caveat": "Anonymized orders from a Brazilian marketplace; the delivered orders were bought Sep 2016 to Aug 2018. Timestamps are taken as recorded; the data does not say who recorded each one, and it has no carrier names and no reasons for delay. The dataset's customers file (customer state) is not used here."
 },
 "numbers": {
  "orders": 99441,
  "delivered_marked": 96478,
  "delivered_no_date": 8,
  "delivered": 96470,
  "delivered_late": 6534,
  "delivered_late_pct": 6.8,
  "stage_sample": 95082,
  "stage_sample_late": 6509,
  "stage_sample_late_pct": 6.8,
  "excluded": {
   "orders": 1388,
   "late": 25,
   "late_pct": 1.8
  },
  "quirk": {
   "missing_approval": 14,
   "missing_handoff": 1,
   "handoff_before_approval": 1350,
   "handoff_before_purchase": 165,
   "delivery_before_handoff": 23,
   "approval_before_purchase": 0
  },
  "reconcile": {
   "reviewed_delivered": 95824,
   "reviewed_late": 6381,
   "reviewed_late_pct": 6.7
  },
  "multi_seller": 1223,
  "multi_category_or_missing_in_one_seller": 181,
  "no_category": 1323,
  "one_seller": 93859,
  "states_total": 22,
  "category_orders": 92355,
  "category_groups_min": 31,
  "categories_total": 73,
  "category_range_all": [
   4.3,
   12.1
  ],
  "months_below_min": {
   "months": 3,
   "orders": 263
  },
  "min_n": 300,
  "sensitivity": {
   "orders": 96281,
   "carrier_leg_share_of_extra": 86.8
  },
  "typical_carrier_days_mar_oct_2017": 8.8,
  "stage_totals": {
   "ontime_days": 11.05,
   "late_days": 33.95,
   "extra_days": 22.9,
   "ontime_median": 9.86,
   "late_median": 31.26,
   "promised_ontime": 23.83,
   "promised_late": 22.64
  },
  "seller": {
   "excess_late_orders": 1341,
   "excess_share_of_late": 20.6,
   "late_orders_seller_on_time": 4695,
   "late_share_seller_on_time": 72.1,
   "late_share_seller_late": 27.9
  },
  "carrier_late": {
   "alone_n": 3916,
   "alone_pct": 60.2,
   "p90_days": 15.3,
   "over_p90_n": 5205,
   "over_p90_pct": 80.0,
   "ontime_carrier_share": 72.4
  },
  "worst_month": {
   "month": "2018-03",
   "orders": 7001,
   "late_pct": 19.0
  },
  "range_big_categories": [
   5.6,
   7.7
  ]
 },
 "stages": [
  [
   "Purchase to approval",
   0.39,
   0.5,
   0.11,
   0.5,
   0.01,
   0.02
  ],
  [
   "Approval to carrier handoff",
   2.65,
   5.57,
   2.92,
   12.8,
   1.79,
   3.07
  ],
  [
   "Carrier handoff to customer",
   8.0,
   27.88,
   19.87,
   86.8,
   6.96,
   26.2
  ]
 ],
 "seller_deadline": [
  [
   "Handed to the carrier by the deadline",
   5.4,
   86383,
   4695,
   90.9,
   72.1
  ],
  [
   "Handed to the carrier after the deadline",
   20.9,
   8699,
   1814,
   9.1,
   27.9
  ]
 ],
 "by_month": [
  [
   "2017-01",
   747,
   2.9,
   9.2,
   22.1
  ],
  [
   "2017-02",
   1638,
   3.0,
   9.6,
   17.3
  ],
  [
   "2017-03",
   2545,
   4.6,
   9.8,
   6.8
  ],
  [
   "2017-04",
   2279,
   6.6,
   11.2,
   7.4
  ],
  [
   "2017-05",
   3530,
   3.0,
   8.3,
   7.3
  ],
  [
   "2017-06",
   3130,
   3.0,
   8.9,
   8.0
  ],
  [
   "2017-07",
   3841,
   2.8,
   8.5,
   7.7
  ],
  [
   "2017-08",
   4190,
   2.9,
   8.1,
   6.8
  ],
  [
   "2017-09",
   4136,
   4.4,
   8.6,
   7.2
  ],
  [
   "2017-10",
   4478,
   4.2,
   8.4,
   7.9
  ],
  [
   "2017-11",
   7287,
   12.4,
   11.1,
   12.9
  ],
  [
   "2017-12",
   5508,
   7.4,
   11.7,
   10.9
  ],
  [
   "2018-01",
   7045,
   5.7,
   10.5,
   10.2
  ],
  [
   "2018-02",
   6543,
   14.2,
   13.4,
   12.3
  ],
  [
   "2018-03",
   7001,
   19.0,
   13.0,
   8.5
  ],
  [
   "2018-04",
   6409,
   4.7,
   8.8,
   6.4
  ],
  [
   "2018-05",
   6681,
   6.6,
   8.7,
   6.6
  ],
  [
   "2018-06",
   5973,
   1.2,
   6.7,
   6.9
  ],
  [
   "2018-07",
   5593,
   3.6,
   6.1,
   9.8
  ],
  [
   "2018-08",
   6265,
   6.2,
   5.2,
   9.3
  ]
 ],
 "by_state": [
  [
   "MA Maranhão",
   369,
   20.1,
   32.8,
   13.1
  ],
  [
   "SP São Paulo",
   66556,
   7.5,
   9.2,
   9.2
  ],
  [
   "RJ Rio de Janeiro",
   4083,
   7.3,
   8.5,
   9.3
  ],
  [
   "ES Espírito Santo",
   306,
   6.2,
   5.2,
   10.6
  ],
  [
   "PR Paraná",
   7211,
   5.5,
   11.3,
   10.0
  ],
  [
   "DF Distrito Federal",
   788,
   5.3,
   8.9,
   9.6
  ],
  [
   "SC Santa Catarina",
   3449,
   5.1,
   8.1,
   10.8
  ],
  [
   "BA Bahia",
   541,
   5.0,
   7.8,
   10.5
  ],
  [
   "MG Minas Gerais",
   7444,
   4.9,
   7.8,
   9.7
  ],
  [
   "PE Pernambuco",
   394,
   3.8,
   4.1,
   10.7
  ],
  [
   "RS Rio Grande do Sul",
   1894,
   3.2,
   8.6,
   8.1
  ],
  [
   "GO Goiás",
   442,
   2.9,
   6.8,
   10.3
  ]
 ],
 "by_category": [
  [
   "Bed bath table",
   8749,
   7.7,
   8.2,
   10.0
  ],
  [
   "Health beauty",
   8400,
   7.7,
   9.1,
   9.1
  ],
  [
   "Watches gifts",
   5322,
   7.6,
   7.2,
   10.0
  ],
  [
   "Auto",
   3692,
   7.5,
   7.4,
   9.5
  ],
  [
   "Furniture decor",
   5963,
   7.4,
   12.0,
   9.5
  ],
  [
   "Telephony",
   3997,
   7.3,
   9.8,
   9.5
  ],
  [
   "Sports leisure",
   7329,
   6.7,
   6.9,
   9.6
  ],
  [
   "Computers accessories",
   6327,
   6.6,
   10.4,
   9.6
  ],
  [
   "Toys",
   3685,
   6.5,
   9.3,
   9.0
  ],
  [
   "Housewares",
   5483,
   5.6,
   9.6,
   8.1
  ]
 ],
 "kpis": [
  {
   "label": "of late orders had a carrier leg longer than the carrier legs of 9 in 10 on-time orders (over 15.3 days)",
   "value": "80.0%",
   "note": "5,205 of 6,509 late orders; the carrier leg is handoff to customer. 95,082 delivered orders"
  },
  {
   "label": "of orders handed to the carrier after the seller's deadline arrived late",
   "value": "20.9%",
   "note": "Against 5.4% of orders handed over by the deadline. 8,699 and 86,383 orders"
  },
  {
   "label": "of late orders were handed to the carrier by the seller's deadline",
   "value": "72.1%",
   "note": "4,695 of 6,509 late orders; the seller was late on the other 27.9%"
  },
  {
   "label": "of orders bought in Mar 2018 arrived late, the worst month",
   "value": "19.0%",
   "note": "7,001 orders that month; the carrier stage averaged 13.0 days, against 8.8 across orders bought Mar to Oct 2017"
  }
 ],
 "sample_queries": [
  {
   "label": "Where the days go: average days per stage, late against on time",
   "sql": "-- Where the days go: average days in each stage, late orders against on-time orders\nWITH deadline AS (          -- one handoff deadline per order: the earliest across its items\n    SELECT order_id, min(shipping_limit_date) AS handoff_deadline\n    FROM olist_order_items\n    GROUP BY order_id\n),\nstages AS (                 -- delivered orders with all three stage timestamps in order\n    SELECT o.order_id,\n           o.order_purchase_timestamp AS purchased,\n           date_diff('day', o.order_estimated_delivery_date::DATE,\n                            o.order_delivered_customer_date::DATE) > 0 AS is_late,\n           o.order_delivered_carrier_date > d.handoff_deadline AS seller_late,\n           epoch(o.order_approved_at - o.order_purchase_timestamp) / 86400 AS days_to_approval,\n           epoch(o.order_delivered_carrier_date - o.order_approved_at) / 86400 AS days_to_handoff,\n           epoch(o.order_delivered_customer_date - o.order_delivered_carrier_date) / 86400 AS days_with_carrier\n    FROM olist_orders o\n    JOIN deadline d USING (order_id)\n    WHERE o.order_status = 'delivered'\n      AND o.order_delivered_customer_date IS NOT NULL\n      AND o.order_approved_at IS NOT NULL\n      AND o.order_delivered_carrier_date >= o.order_approved_at      -- also drops a missing handoff date\n      AND o.order_delivered_customer_date >= o.order_delivered_carrier_date\n)\nSELECT CASE WHEN is_late THEN 'Late' ELSE 'On time or early' END AS delivery,\n       count(*) AS orders,\n       round(avg(days_to_approval), 2) AS purchase_to_approval,\n       round(avg(days_to_handoff), 2) AS approval_to_handoff,\n       round(avg(days_with_carrier), 2) AS handoff_to_customer\nFROM stages\nGROUP BY is_late\nORDER BY is_late",
   "output": {
    "columns": [
     "delivery",
     "orders",
     "purchase_to_approval",
     "approval_to_handoff",
     "handoff_to_customer"
    ],
    "rows": [
     [
      "On time or early",
      88573,
      0.39,
      2.65,
      8.0
     ],
     [
      "Late",
      6509,
      0.5,
      5.57,
      27.88
     ]
    ]
   }
  },
  {
   "label": "Did the seller meet the handoff deadline, and did the order arrive late?",
   "sql": "-- Did the seller meet the handoff deadline, and did the order arrive late?\nWITH deadline AS (          -- one handoff deadline per order: the earliest across its items\n    SELECT order_id, min(shipping_limit_date) AS handoff_deadline\n    FROM olist_order_items\n    GROUP BY order_id\n),\nstages AS (                 -- delivered orders with all three stage timestamps in order\n    SELECT o.order_id,\n           o.order_purchase_timestamp AS purchased,\n           date_diff('day', o.order_estimated_delivery_date::DATE,\n                            o.order_delivered_customer_date::DATE) > 0 AS is_late,\n           o.order_delivered_carrier_date > d.handoff_deadline AS seller_late,\n           epoch(o.order_approved_at - o.order_purchase_timestamp) / 86400 AS days_to_approval,\n           epoch(o.order_delivered_carrier_date - o.order_approved_at) / 86400 AS days_to_handoff,\n           epoch(o.order_delivered_customer_date - o.order_delivered_carrier_date) / 86400 AS days_with_carrier\n    FROM olist_orders o\n    JOIN deadline d USING (order_id)\n    WHERE o.order_status = 'delivered'\n      AND o.order_delivered_customer_date IS NOT NULL\n      AND o.order_approved_at IS NOT NULL\n      AND o.order_delivered_carrier_date >= o.order_approved_at      -- also drops a missing handoff date\n      AND o.order_delivered_customer_date >= o.order_delivered_carrier_date\n)\nSELECT CASE WHEN seller_late THEN 'After the deadline' ELSE 'By the deadline' END AS handoff,\n       count(*) AS orders,\n       count(*) FILTER (WHERE is_late) AS late_orders,\n       round(100.0 * count(*) FILTER (WHERE is_late) / count(*), 1) AS pct_of_these_orders_late\nFROM stages\nGROUP BY seller_late\nORDER BY seller_late",
   "output": {
    "columns": [
     "handoff",
     "orders",
     "late_orders",
     "pct_of_these_orders_late"
    ],
    "rows": [
     [
      "By the deadline",
      86383,
      4695,
      5.4
     ],
     [
      "After the deadline",
      8699,
      1814,
      20.9
     ]
    ]
   }
  },
  {
   "label": "Late orders by purchase month",
   "sql": "-- Late orders by purchase month (months with 300 or more orders)\nWITH deadline AS (          -- one handoff deadline per order: the earliest across its items\n    SELECT order_id, min(shipping_limit_date) AS handoff_deadline\n    FROM olist_order_items\n    GROUP BY order_id\n),\nstages AS (                 -- delivered orders with all three stage timestamps in order\n    SELECT o.order_id,\n           o.order_purchase_timestamp AS purchased,\n           date_diff('day', o.order_estimated_delivery_date::DATE,\n                            o.order_delivered_customer_date::DATE) > 0 AS is_late,\n           o.order_delivered_carrier_date > d.handoff_deadline AS seller_late,\n           epoch(o.order_approved_at - o.order_purchase_timestamp) / 86400 AS days_to_approval,\n           epoch(o.order_delivered_carrier_date - o.order_approved_at) / 86400 AS days_to_handoff,\n           epoch(o.order_delivered_customer_date - o.order_delivered_carrier_date) / 86400 AS days_with_carrier\n    FROM olist_orders o\n    JOIN deadline d USING (order_id)\n    WHERE o.order_status = 'delivered'\n      AND o.order_delivered_customer_date IS NOT NULL\n      AND o.order_approved_at IS NOT NULL\n      AND o.order_delivered_carrier_date >= o.order_approved_at      -- also drops a missing handoff date\n      AND o.order_delivered_customer_date >= o.order_delivered_carrier_date\n)\nSELECT strftime(purchased, '%Y-%m') AS purchase_month,\n       count(*) AS orders,\n       round(100.0 * count(*) FILTER (WHERE is_late) / count(*), 1) AS pct_of_orders_late,\n       round(avg(days_with_carrier), 1) AS avg_days_with_carrier\nFROM stages\nGROUP BY 1\nHAVING count(*) >= 300\nORDER BY 1",
   "output": {
    "columns": [
     "purchase_month",
     "orders",
     "pct_of_orders_late",
     "avg_days_with_carrier"
    ],
    "rows": [
     [
      "2017-01",
      747,
      2.9,
      9.2
     ],
     [
      "2017-02",
      1638,
      3.0,
      9.6
     ],
     [
      "2017-03",
      2545,
      4.6,
      9.8
     ],
     [
      "2017-04",
      2279,
      6.6,
      11.2
     ],
     [
      "2017-05",
      3530,
      3.0,
      8.3
     ],
     [
      "2017-06",
      3130,
      3.0,
      8.9
     ],
     [
      "2017-07",
      3841,
      2.8,
      8.5
     ],
     [
      "2017-08",
      4190,
      2.9,
      8.1
     ],
     [
      "2017-09",
      4136,
      4.4,
      8.6
     ],
     [
      "2017-10",
      4478,
      4.2,
      8.4
     ],
     [
      "2017-11",
      7287,
      12.4,
      11.1
     ],
     [
      "2017-12",
      5508,
      7.4,
      11.7
     ],
     [
      "2018-01",
      7045,
      5.7,
      10.5
     ],
     [
      "2018-02",
      6543,
      14.2,
      13.4
     ],
     [
      "2018-03",
      7001,
      19.0,
      13.0
     ],
     [
      "2018-04",
      6409,
      4.7,
      8.8
     ],
     [
      "2018-05",
      6681,
      6.6,
      8.7
     ],
     [
      "2018-06",
      5973,
      1.2,
      6.7
     ],
     [
      "2018-07",
      5593,
      3.6,
      6.1
     ],
     [
      "2018-08",
      6265,
      6.2,
      5.2
     ]
    ]
   }
  },
  {
   "label": "Late orders: the carrier stage against the promise and against on-time orders",
   "sql": "-- Late orders: the carrier stage against the promise and against on-time orders\nWITH deadline AS (          -- one handoff deadline per order: the earliest across its items\n    SELECT order_id, min(shipping_limit_date) AS handoff_deadline\n    FROM olist_order_items\n    GROUP BY order_id\n),\nstages AS (                 -- delivered orders with all three stage timestamps in order\n    SELECT o.order_id,\n           o.order_purchase_timestamp AS purchased,\n           (o.order_purchase_timestamp + (o.order_delivered_customer_date - o.order_delivered_carrier_date))::DATE\n               > o.order_estimated_delivery_date::DATE AS carrier_alone_late,\n           date_diff('day', o.order_estimated_delivery_date::DATE,\n                            o.order_delivered_customer_date::DATE) > 0 AS is_late,\n           o.order_delivered_carrier_date > d.handoff_deadline AS seller_late,\n           epoch(o.order_approved_at - o.order_purchase_timestamp) / 86400 AS days_to_approval,\n           epoch(o.order_delivered_carrier_date - o.order_approved_at) / 86400 AS days_to_handoff,\n           epoch(o.order_delivered_customer_date - o.order_delivered_carrier_date) / 86400 AS days_with_carrier\n    FROM olist_orders o\n    JOIN deadline d USING (order_id)\n    WHERE o.order_status = 'delivered'\n      AND o.order_delivered_customer_date IS NOT NULL\n      AND o.order_approved_at IS NOT NULL\n      AND o.order_delivered_carrier_date >= o.order_approved_at      -- also drops a missing handoff date\n      AND o.order_delivered_customer_date >= o.order_delivered_carrier_date\n),\np90 AS (                    -- 90th percentile of the on-time orders' carrier stage\n    SELECT quantile_disc(days_with_carrier, 0.9) AS days FROM stages WHERE NOT is_late\n)\nSELECT count(*) AS late_orders,\n       count(*) FILTER (WHERE carrier_alone_late) AS carrier_alone_over_promise,\n       round(100.0 * count(*) FILTER (WHERE carrier_alone_late) / count(*), 1) AS pct_of_late_orders,\n       count(*) FILTER (WHERE days_with_carrier > (SELECT days FROM p90)) AS over_p90,\n       round(100.0 * count(*) FILTER (WHERE days_with_carrier > (SELECT days FROM p90)) / count(*), 1) AS pct_over_p90,\n       round((SELECT days FROM p90), 1) AS p90_days\nFROM stages\nWHERE is_late",
   "output": {
    "columns": [
     "late_orders",
     "carrier_alone_over_promise",
     "pct_of_late_orders",
     "over_p90",
     "pct_over_p90",
     "p90_days"
    ],
    "rows": [
     [
      6509,
      3916,
      60.2,
      5205,
      80.0,
      15.3
     ]
    ]
   }
  }
 ],
 "cleaning": [
  "The file has 99,441 orders. 96,478 are marked delivered; 8 of those have no delivery date and are left out, which leaves 96,470 delivered orders. Orders that were canceled, unavailable, shipped but never marked delivered or not yet shipped are not part of this page.",
  "Late means delivered 1 or more calendar days after the estimated delivery date shown at purchase, using the same rule as the Late deliveries page: 6,534 of 96,470 delivered orders (6.8%). That page shows 6,381 of 95,824 (6.7%) because it counts only delivered orders that have a review.",
  "Three stages, from three timestamps: purchase to approval (order_approved_at), approval to handoff to the carrier (order_delivered_carrier_date), and handoff to the customer (order_delivered_customer_date). Days are the exact time difference divided by 86,400 seconds.",
  "Stage timestamps: I leave 1,388 delivered orders out of the stage tables because a timestamp is missing or out of order (14 with no approval time, 1 with no handoff time, 1,350 with the handoff dated before the approval, 23 delivered before the handoff). 95,082 orders remain, 6,509 of them late (6.8%). Every chart and table below starts from these 95,082 orders.",
  "The seller's handoff deadline is the order's earliest shipping_limit_date across its items. The seller was late when the carrier handoff time is after that deadline. The handoff time is one date for the whole order.",
  "1,223 of the 95,082 orders have more than one seller. I keep them in the stage and deadline views (their earliest deadline is used) and leave them out of the seller state and category views, which need one seller and one category.",
  "Seller state and product category: groups with fewer than 300 orders are not shown. Product category comes from the products file, with English names from the translation file. 1,504 single-seller orders are left out of the category view: 181 with more than one category or a mix of named and unnamed items, and 1,323 with no category at all.",
  "Two more figures about the carrier stage of late orders. First, the order's purchase time plus its carrier stage (handoff to customer), as a calendar date, is compared with the estimated delivery date: when it is already later, the carrier stage alone took longer than the whole delivery time promised at purchase (3,916 of 6,509 late orders, 60.2%). Second, the 90th percentile of the on-time orders' carrier stage is 15.3 days (nearest rank, 88,573 on-time orders); 5,205 late orders (80.0%) had a longer carrier stage.",
  "A rate is the share of the orders in that group. The group is named in every label."
 ],
 "checks": [
  "Orders, order items, sellers, products and the category translation match the file sizes Kaggle lists (99,441 orders).",
  "Late deliveries page reconciled: the 95,824 delivered orders that have a review (using the reviews file credited on the Late deliveries page), counted the way that page counts them, have 6,381 late (6.7%). This page counts all 96,470 delivered orders, reviewed or not, so its late rate is 6.8%.",
  "Sensitivity: merging approval into the handoff stage (purchase to handoff) keeps the 96,281 delivered orders whose dates are in order from purchase, including those with the handoff dated before the approval; the carrier stage is still 86.8% of the extra days, the same as in the stage tables.",
  "Every number on this page was recomputed by separate code (verify_stuck.py: plain Python reading the CSV files with the csv module, with no DuckDB and no intermediate files) and matches.",
  "The four queries below were run in DuckDB 1.5.5 on the Olist files loaded as tables named like the files; each returns the figures shown under it."
 ]
}