|
8 | 8 | value, the timestamps of the previous tag assignment and the current |
9 | 9 | observation, and a flag for whether the tag changed. |
10 | 10 |
|
| 11 | +At US scale the versions + changes Parquets together exceed typical RAM, so |
| 12 | +this script uses ``format_observations_duckdb`` to pivot changes wide, LEFT |
| 13 | +JOIN them against versions, and ORDER BY (type, id, version) inside DuckDB, |
| 14 | +spilling the sort to disk as needed. A Python scan then runs the per-POI |
| 15 | +state machine over the sorted row stream and writes observations directly to |
| 16 | +the output CSV — peak RSS stays bounded to roughly ``DUCKDB_MEMORY_LIMIT``. |
| 17 | +
|
11 | 18 | Config keys used (config.yaml): |
12 | 19 | directories.osm_data — directory containing input and output files |
13 | 20 | download.osm.filter_keys — all tag keys collected (passed as keep_keys) |
|
24 | 31 | plus all keep_keys columns for grouping |
25 | 32 | """ |
26 | 33 |
|
27 | | -import pandas as pd |
28 | 34 | from config_versioned import Config |
29 | 35 |
|
30 | | -from openpois.osm.format_observations import format_observations |
| 36 | +from openpois.osm.format_observations import format_observations_duckdb |
31 | 37 |
|
32 | 38 |
|
33 | 39 | # ---------------------------------------------------------------------------------------- |
|
40 | 46 | OSM_KEYS = config.get("download", "osm", "filter_keys") |
41 | 47 | TAG_KEY = config.get("osm_data", "tag_key") |
42 | 48 |
|
| 49 | +CHANGES_PATH = config.get_file_path("osm_data", "osm_changes") |
| 50 | +VERSIONS_PATH = config.get_file_path("osm_data", "osm_versions") |
| 51 | +OUT_PATH = SAVE_DIR / f"osm_observations_{TAG_KEY}.csv" |
| 52 | + |
| 53 | +# DuckDB execution limits. The sort operator spills past memory_limit so this |
| 54 | +# caps peak RAM independent of input size. Threads default to os.cpu_count() |
| 55 | +# when left as None. |
| 56 | +DUCKDB_MEMORY_LIMIT = "4GB" |
| 57 | +DUCKDB_THREADS = None |
| 58 | + |
43 | 59 |
|
44 | 60 | # ---------------------------------------------------------------------------------------- |
45 | 61 | # Main workflow |
46 | 62 | # ---------------------------------------------------------------------------------------- |
47 | 63 |
|
48 | 64 | if __name__ == "__main__": |
49 | | - # Read files |
50 | | - changes_df = pd.read_parquet(config.get_file_path("osm_data", "osm_changes")) |
51 | | - versions_df = pd.read_parquet(config.get_file_path("osm_data", "osm_versions")) |
52 | | - |
53 | | - # Format changes and versions into observations |
54 | | - observations_df = format_observations( |
55 | | - changes_df = changes_df, |
56 | | - versions_df = versions_df, |
| 65 | + n_written = format_observations_duckdb( |
| 66 | + changes_path = CHANGES_PATH, |
| 67 | + versions_path = VERSIONS_PATH, |
| 68 | + output_path = OUT_PATH, |
57 | 69 | tag_key = TAG_KEY, |
58 | 70 | keep_keys = OSM_KEYS, |
| 71 | + duckdb_memory_limit = DUCKDB_MEMORY_LIMIT, |
| 72 | + duckdb_threads = DUCKDB_THREADS, |
59 | 73 | ) |
60 | | - |
61 | | - # Save observations |
62 | | - out_path = SAVE_DIR / f"osm_observations_{TAG_KEY}.csv" |
63 | | - observations_df.to_csv(out_path, index = False) |
64 | | - print(f"Saved {len(observations_df)} observations to {out_path}") |
| 74 | + print(f"Saved {n_written} observations to {OUT_PATH}") |
0 commit comments