← README · Why this dataset · Architecture · Results
I measured the release file by file instead of trusting its paper or its readme. Five things came out of that, and one of them changed the entire evaluation design.
| Finding | Consequence | |
|---|---|---|
| A | The released graph covers the second week only | The evaluation lives inside week 2 |
| B | The release is 57.4% of the users the paper reports | Every number here is against what is downloadable, never the paper's larger set |
| C | Three of eight relations are empty in the order files — including one worth 38.41% of the authors' edges | Two graph views are reported: what I can rebuild, and what they shipped |
| D | CRLF makes the last column lie — would have fabricated ~14 billion phantom edges | Caught by a test that now pins it |
| E | The two order files are separately re-indexed from zero | The single most consequential finding — see below |
Download: make download fetches all 4.00 GB from
OSF and
md5-checks every file.
| File | Bytes | Rows | What it is |
|---|---|---|---|
node.csv |
142,373,615 | 3,267,961 | Users, labels, placeholder feature matrix |
edge.csv |
675,362,394 | 10,012,449 | The authors' pre-built user–user graph |
Transactions/order_train.csv |
1,625,195,771 | 22,456,547 | Week-1 raw orders |
Transactions/order_test.csv |
1,559,383,792 | 21,478,704 | Week-2 raw orders |
order_test.csv contains 3,267,961 distinct users with ids 0…3,267,960, and
node.csv has 3,267,961 rows with contiguous ids 0…3,267,960. Same user
set. The shipped graph is the week-2 slice.
| Week 1 | Week 2 | |
|---|---|---|
| Orders | 22,456,547 | 21,478,704 |
| Distinct users | 3,785,628 (0…3,785,627) |
3,267,961 (0…3,267,960) |
| Date range | 1000-05-13 → 1000-05-20 |
1000-05-20 → 1000-05-28 |
1000-05-20 is the only shared date — 2,935,107 orders in week 1 and 75 in
week 2 (0.000349% of it). Week 1 is cut <= 1000-05-20, week 2 >= 1000-05-21.
(Timestamps are year-shifted to 1000, so pandas.to_datetime cannot represent
them — and they are day-resolution, which is why nothing in this repository
claims anything about sub-day bursts.)
| Paper | Release | |
|---|---|---|
| Users | 5,693,351 | 3,267,961 (57.4%) |
| Edges | ~29,000,000 | 10,012,449 (34.5%) |
| Fraud-labelled | ~95,700 (1.68%) | 68,533 (2.097%) |
| Normal-labelled | ~405,000 (7.12%) | 237,084 (7.255%) |
| Unlabelled | rest | 2,962,344 (90.648%) |
Not a complaint — the fraud rate is actually higher in the release (2.10% vs 1.68%). But nothing in this project describes itself as running on 5.7M accounts. It runs on 3.27M users and 10.0M edges.
r2, r4 and r5 have zero non-null values across all 43.9M orders —
yet r4 alone is 38.41% of the authors' edge.csv.
| Rel | Meaning | Present in orders | Share of edge.csv |
|---|---|---|---|
| r1 | order-location geohash | 99.15% / 99.26% | 5.52% |
| r2 | shared links | empty | 0.82% |
| r3 | delivery info | 99.99% / 99.99% | 0.04% |
| r4 | retail store | empty | 38.41% |
| r5 | group id | empty | 0.94% |
| r6 | promotion id | 48.85% / 47.30% | 73.95% |
| r7 | coupon type | 99.99% / 99.99% | 8.55% |
| r8 | stimulation id | 99.24% / 99.13% | 5.82% |
So the pipeline reports two views: the five-relation graph I can rebuild
from orders, and the authors' eight-relation edge.csv as shipped. What the
missing three are worth is then a measured number, not a guess
(results.md).
Week-1 user 12,345 and week-2 user 12,345 are different people, and no key joins them.
I found this the hard way: a scorer that trained cleanly and learned exactly nothing, because it joined week-1 behaviour to week-2 labels through an id that does not carry across (the full account). Three checks establish it:
- Both id spaces are perfectly dense. Week 1 covers exactly
0…3,785,627; week 2 covers exactly0…3,267,960. Not one gap in either. A smaller set drawn from a shared population would be sparse, not contiguous — both files were renumbered from zero. - Behaviour does not correlate across the boundary. For the same id,
week-1 vs week-2
n_orderscorrelates at +0.0101,degreeat +0.0013,core_numberat +0.0008 — indistinguishable from independent. - Week 2 is internally consistent. The graph built from
order_test.csvshares 3,079,704 pairs with the shippededge.csv(30.8%, far above chance). Orders, graph and labels agree. Only week 1 stands apart.
What follows. Labels live in the week-2 id space, so a week-1-train /
week-2-test split is not possible on this release — the obstacle is the id
column, not the calendar. Week 2 carries the evaluation, split account-disjoint
and temporally forward within itself: train on 05-21…05-24, score held-out
accounts on 05-25…05-28. Both properties the original design wanted, using
the only data that can support them.
Every line ends \r\n, and r8 is the last column. When r8 is empty the
final field is not "" but "\r". My first pass reported r8 present on
100.00% of orders. It is 99.24%.
Why this matters more than it sounds: the graph builder connects every pair of
users sharing an entity. Had the loader kept the \r, every order with no r8
would have shared one phantom entity — and the builder would have emitted a
clique across all of them. Roughly 14 billion fabricated edges from one
unstripped byte, every one looking like dense, suspicious, ring-shaped
structure. Worse, the entity cap would have silently dropped the phantom entity
as "too common", so the bug would have vanished from the output without ever
being fixed. tests/test_schema.py now pins r8 between 98% and 99.5%.
| Rel | Distinct entities | Largest entity | Uncapped pairs | @cap 100 |
|---|---|---|---|---|
| r1 | 2,023,601 | 862,317 | 371,808,329,847 | 11,460,780 |
| r3 | 3,274,955 | 7 | 12,122 | 12,122 |
| r6 | 390,153 | 7,421 | 3,193,225,490 | 48,405,948 |
| r7 | 37,030 | 3,187,247 | 5,086,273,447,218 | 2,248,983 |
| r8 | 10,496,292 | 1,618 | 36,071,829 | 9,682,878 |
| Total | 5,461,311,086,506 | 71,810,711 |
- Uncapped is not slow — it is impossible. 5.46 trillion pairs on week 2.
r7alone is 5.09 trillion of them, because one coupon type is shared by 3,187,247 users: 97.5% of the entire user base. A relation connecting 97.5% of users is not evidence of anything. - A cap of 500 was ~8× too loose — 568M pairs, which does not fit in 16 GB.
100 leaves 71.8M and is the configured default, in
config/default.yaml. - Relations are wildly unequal as evidence.
r3never exceeds seven users per entity — close to a private key, so two users sharing one is strong evidence (and it contributes just 12,122 pairs total).r7is near-universal and nearly worthless. The rarity weight1/log(2+|users(e)|)orders them correctly without hand-ranking.
Two entities look like sentinels, not real values — the largest r1 holds
26.4% of everyone and the largest r7 holds 97.5%. Almost certainly "unknown"
placeholders. The cap removes them as a side effect, but they are excluded
explicitly too, so the reason lives in the code rather than in where a
threshold happened to land.
- Week 2 carries the evaluation (finding E). Account-disjoint, temporally forward within the week.
- Every node feature is engineered from orders — the dataset ships none (every
node.csvfeature column is literally 1.0). - Two graph views are always reported, which is what makes the three unbuildable relations a measurement rather than a guess.
- Labels are static, user-level, week-2 only.
-1means unknown, is excluded from metrics, and is never counted as normal. - Day-resolution timestamps — so no hour-of-day features and no sub-day burst claims anywhere.
← README · Next: why this dataset at all →