DataPrep_EDA
The project uses two published research datasets. Both were downloaded as files from the pages their authors maintain, and no API was involved.
Where the data comes from
YJMob100K
The main dataset is YJMob100K, published by Takahiro Yabe and six co-authors in 2024 together with a paper in the journal Scientific Data. This project uses its second file, which follows 25,000 people for 75 days. Each day is cut into 48 half-hour slots, and the area is a grid of 200 by 200 cells, each 500 m wide. One row says that a person was seen in a cell during a slot. The same release has a file that counts places of 85 kinds in every cell.
The authors state that the data comes from Japan. They withhold the city and the dates, and they do not say what the emergency on days 60 to 74 was. This project does not try to find out. The dataset was chosen because it records the same people before and during a disruption, which is what the questions on the Introduction tab need.
The files were downloaded from Zenodo, where they are published under the CC BY 4.0 licence. Their MD5 checksums are verified at the start of every run of the preparation script.
Foursquare check-ins in Tokyo
The second dataset holds 573,703 Foursquare check-ins made in Tokyo by 2,293 users between April 2012 and February 2013, collected by Dingqi Yang and colleagues. A check-in is a voluntary post that says a user is at a named venue, and every venue has a category such as Train Station or Coffee Shop. It was added because YJMob100K has no place names. In the Tokyo data the kind of place is known, so a method that guesses the kind of place from time and location can be tested against a true answer before anything similar is tried on anonymous cells.
Tokyo was picked because both datasets come from Japan. The two are never matched on a map.
Links to the raw data
- YJMob100K on Zenodo, record 10836269. Files used:
yjmob100k-dataset2.csv.gz,cell_POIcat.csv.gzandPOI_datacategories.csv. Paper: Yabe et al. 2024, Scientific Data 11, 397. - NYC and Tokyo Check-in Dataset on Dingqi Yang’s page. File used:
dataset_TSMC2014_TKY.txtinsidedataset_tsmc2014.zip. The authors ask for this paper to be cited: Dingqi Yang, Daqing Zhang, Vincent W. Zheng and Zhiyong Yu, Modeling User Activity Preference by Leveraging User Spatial Temporal Characteristics in LBSNs, IEEE Transactions on Systems, Man, and Cybernetics: Systems 45(1), 129-142, 2015.
The raw data
The first eight rows of the raw YJMob100K file, one row per observation: person (uid), day (d), half-hour slot (t) and grid cell (x, y). The slot numbers skip 3, 4, 6, 7 and 10, which is how half-hours without a record appear in the file.
The first eight rows of the raw places file: a grid cell, a category number from 1 to 85, and how many places of that category the cell holds. The categories are numbers only, because the authors do not publish which name belongs to which number.
The first six rows of the raw Tokyo check-in file: anonymised user, venue, venue category, coordinates, time-zone offset in minutes and the time in UTC. The local hour of a visit is not in the file and has to be worked out from the last two columns.
Cleaning
YJMob100K: checks first, then flags
The raw file has 29,389,749 rows. It has no missing values, no duplicate rows and no person with two records in the same slot. Every value lies inside the documented range and the file is already sorted by person and time. There was nothing to repair, so cleaning here means adding the columns that later analysis needs and marking records that call for caution. No row is deleted.
Only 32.7% of all person-slots have a record (Figure 1). Where a person was during an unrecorded half-hour is unknown, and nothing on this site fills those gaps in.
The share of all person-slots that have a record, for each of the 75 days, with filled dots on weekend-like days and open dots on holiday-like days. The weekly rhythm runs through the whole period, the level drops from day 60, and day 27 is the lowest day of all.
The steps, in the order the script runs them:
- Time columns are added: the type of day (described in the next section), one of four windows (days 0-44, 45-59, 60-66 and 67-74) that later separate what a model may learn from and what it is tested on, and a running slot number.
- Each record is linked to the same person’s previous record, with the gap in slots and the distance in cells. A record that follows the previous one by exactly one slot is marked
adjacent. That is true for 69.8% of records, and movement is only measured on those. - Moves of more than 100 cells (50 km) within 30 minutes are marked
big_jump. They make up 0.04% of records. The distribution of move sizes has a smooth tail with no move above 100 km, so these are treated as fast long trips and kept. - One-cell moves that are back in the starting cell one slot later are marked
flicker_mid, 3.5% of records. One-cell moves return 27.6% of the time, against 2.5 to 4% for moves of five cells or more (Figure 7). The working explanation is that a person near a cell border gets logged on either side of it. This is a hypothesis about data quality, and the flag lets any later result be checked with and without these records. - People are split once, by person, into a development group (70%, 17,500 people) and an evaluation group (7,500 people) with the fixed seed 20260917. A person is eligible for later evaluation with at least 20 adjacent pairs of records in every window. The rule was set on the development group, where it keeps 96.3% of people, and it keeps 95.7% of the evaluation group.
Types of day
The dataset has no calendar, so each day gets a weekly phase and a type from the data itself. On days 0 to 59 the phase is the day number modulo 7. On days 60 to 74 it is the day number plus four, modulo 7, because the weekly rhythm of the emergency block is shifted by four days. Phases 0 and 6 are weekend-like, 21 days in all, among them 65, 66, 72 and 73 in the emergency. Six days (1, 8, 29, 37, 50 and 67) sit on a weekday phase but run like a rest day and are marked holiday_like. Every other day is a workday: 38 of days 0 to 59 and 10 of days 60 to 74.
The author set these rules from two signals in the data that do not depend on how many people are observed. The first is the shape of the day: the observation rate between 07:30 and 08:30 divided by the rate between 10:00 and 11:30. All 75 days fall into two bands with nothing between them, above 1.05 with a morning peak or below 0.90 without one (Figure 2).
For each day, the observation rate at 07:30-08:30 divided by the rate at 10:00-11:30. Every workday sits above the dashed line and every weekend-like or holiday-like day below it, before day 60 and after.
The second signal is location. For 24,245 people a home cell can be set as the cell where they are most often recorded between 00:00 and 06:00 on days 0 to 59. On workdays about 8.5% of the records between 10:00 and 16:00 are at that cell. On rest days it is 12 to 15%. This sorts the 75 days the same way as the morning ratio.
The four-day shift comes from an alignment test. Each day’s profile over the 48 slots was compared with the typical profile of every day of the week on days 0 to 59, for all seven possible shifts. On days 45 to 59, where the right answer is a shift of zero, the test returns zero. On days 60 to 74 a shift of four days fits best by a wide margin (error 0.97, against 1.59 for the next best shift). The weekly rhythm shifts by four days at day 60. The dataset paper gives no dates, so the shift is reported as a property of the data and no reason is offered.
The six holiday-like days have no morning peak and low coverage, like a public holiday. Day 27 is a weekend-like day with the lowest coverage of all 75 days, 22.6%. It stays in the data and is left out of averages by type of day.
The preparation script checks the typing on every run. It computes the morning ratio for every day and stops with an error if any day disagrees with its type.
A sweep over every day and slot found two recording artefacts. Day 66 has a gap at 23:00, with 1.4% of people observed against 14% on day 73, the matching day a week later. The 00:00 slot is thin on all of days 60 to 74, with 9.3% of people observed against 13.8% on days 0 to 59. Both are documented, and no records are removed.
With the days typed, like can be compared with like (Figure 3). In the emergency people are observed less on every kind of day: 10.4% less on workdays, from 35.7% of slots to 32.0%, and 12 to 17% less on rest days. The shape of the day holds. Workdays keep their 08:00 peak, at 49.0% of people observed against 53.8%, and it is still the highest point of the day.
The share of people observed in each half-hour, averaged over workdays and over weekend-like days, before day 60 (solid) and from day 60 (dashed). Each dashed curve is a lower copy of its solid one, and workdays keep the 08:00 peak.
Places per cell
The places file has 221,159 rows in long form: cell, category number, count. It was turned into a table with one row per cell and one column per category, 20,146 cells by 85 categories, and joined to the 30,277 cells where anyone was ever recorded. Occupied cells without a row in the places file get zeros and has_poi = False. They hold 2.8% of all records. The 85 category names ship as a plain list without saying which number is which, so the numbers are kept as they are and no names are attached.
Foursquare Tokyo
- 577 exact duplicate rows were dropped, which leaves 573,126 check-ins.
- Local time was set to Japan time (UTC+9) for every row. 27 check-ins carry a different offset although the venue is in Tokyo. They are kept and marked
tz_flag. - Stray whitespace in category names was normalised, which leaves 247 distinct names.
- Coordinates were binned to a local grid of 500 m cells, 80 by 80, with 3,853 cells in use. The grid has its own arbitrary origin and is never aligned with the YJMob100K grid. Only the cell size is shared.
- Hour, weekday, weekend and month were derived from the local time.
- The 247 category names were folded into eight coarse place types plus Other, which holds 167 check-ins. The grouping uses keyword rules. The rules were drafted with the help of a large language model (Claude), every one of the 247 assignments was then reviewed by hand, and the full mapping is published with the code so it can be audited.
The cleaned data
The same kind of rows after cleaning, here for person 1, with the derived columns to the right of the original five. Each record now carries its day type, its window, the link to its previous record and the three flags described above.
The cell table after cleaning, with one row per occupied grid cell and one column per category (9 of 87 columns shown). The column has_poi says whether the cell had any row in the raw places file, and poi_total is the sum over all 85 categories.
The check-in table after cleaning (10 of 15 columns shown). Each check-in now carries a coarse place type (group), the local time, a 500 m grid cell (cx, cy) and weekday fields.
The five cleaned tables can be downloaded from the release Processed tables, Part 1 on GitHub. The four YJMob100K tables are shared under CC BY 4.0, like their source. The Foursquare table follows its source, which states no licence and asks for the paper to be cited. The preparation script rebuilds all five from the original downloads in about 35 seconds.
A first look at YJMob100K
One dot per person: the share of half-hour slots with a record on days 45-59 (horizontal) against days 67-74 (vertical). Most of the cloud lies below the dashed diagonal, so most people are recorded less often in the emergency.
The number of records each of the 25,000 people has over the 75 days. The median is 1,106 out of 3,600 possible half-hours, and nobody has fewer than 571.
How many grid cells a person moves between two records exactly 30 minutes apart, on a logarithmic scale. The bars fall away smoothly, and a move of one cell (25.8%) is more common than staying in the same cell (18.9%).
Of all moves of a given size, the share that are back in the starting cell one slot later. One-cell moves return 28% of the time against 2.5 to 4% for moves of five cells or more, which is the evidence behind the flicker flag.
All 29.4 million records counted per 500 m cell of the 200 by 200 grid, on a logarithmic colour scale. Records fall in 30,277 of the 40,000 cells, and white cells have none.
The number of places in each cell, on the same grid and a logarithmic colour scale. Cells with many places are also the cells with many records in Figure 8.
The 20 largest of the 85 numbered place categories, by number of places in the whole area. The largest has 42,806 places and the twentieth about 8,400, while the smallest category of all 85 has a single place.
A first look at Foursquare Tokyo
Tokyo check-ins per month from April 2012 to February 2013. September 2012 has about 10,000 check-ins while every other month has more than 26,000, which points to a gap in how the data was collected.
The share of check-ins in each of the eight coarse place types. Transit takes 45% and Home under 1%, so the types are far from evenly represented.
For each place type, how its check-ins spread over the 24 hours of the day. Transit peaks at 08:00 and 18:00 while Nightlife has a single peak around 19:00, so the hour of a visit already says something about the kind of place.
The mix of place types on weekdays and at weekends. Transit falls from 47% to 40% at the weekend and Shopping/Services grows from 14% to 18%.
Check-ins per cell on the local grid of 500 m cells, on a logarithmic colour scale. The occupied cells form a circle, because check-ins were collected within a fixed radius of a centre.
One dot per user: days with at least one check-in against the total number of check-ins, on a logarithmic scale. Every user has at least 100 check-ins, and active days range from about 10 to about 220.
Code
All code is Python 3.11 with pandas, NumPy, PyArrow and Matplotlib. It ran on the Alpine cluster at the University of Colorado Boulder.
notebooks/01_first_look.ipynb: first look at both datasets, the checks, the first version of the cleaning, and the figures and table images on this page.notebooks/02_review_and_fix.ipynb: the day-type review, the corrected cleaning, and Figures 1 to 3.code/prep.py: the preparation script. It checks the raw files against their MD5 checksums and rebuilds every cleaned table. The commit history of this file shows the first version and the fix.code/foursquare_category_groups.csv: the reviewed mapping from 247 Foursquare category names to eight place types.