v0.5.0
·
234 commits
to main
since this release
Added
converter.output_columns: restricts pandas to materializing only the configured columns while parsing CSV, viaread_csv'susecols, instead of parsing every column and discarding the unwanted ones afterward.converter.max_workers_by_datasetoverrides the scalarconverter.max_workersfor a single dataset, since a safe worker count depends on peak per-worker memory and that varies a lot by datasetfilter.output_columns: per-dataset column projection on the filtered output, independent ofcolumns_to_check's row-filtering.filter.compressionalso becomes a per-dataset configurable write codec (pyarrow already ships zstd/gzip/brotli/lz4, no new dependency needed)filter.float32_columns: opt-in, per-dataset, per-column narrowing offloat64output columns tofloat32. Off by default: real GDELT float data,AvgToneespecially, has been observed with up to 15 significant figures, well pastfloat32's ~7, and a round-trip test against 6.5M real rows changed the value on 31 to 100% of rows depending on the column. Narrowing low-cardinality integer columns was evaluated too and not implemented, since Parquet already dictionary-encodes those regardless of declared type and the measured gain was under 1%converter.compression: configurable Parquet codec forconvert's own writes, same per-dataset shapefilter.compressionalready had, defaulting to zstd for the same measured reason that default was chosen therecrossrefwarns before a configuredoutput_columnsstrips a dataset's required join key (GlobalEventIDforgdelt_event,EventIdsfor both GKG 1.0 datasets,V2DOCUMENTIDENTIFIERforgdelt_gkg_v2,GLOBALEVENTID/MentionIdentifierforgdelt_mentions), checked by bothrun_filterandrun_converterat their own configure time against a singleREQUIRED_JOIN_COLUMNSmapping.crossrefalready raised a clear error for this, but only at join time, possibly after an unrelatedsamplerun had already completed on the pruned outputcrossref_events_gkg_v1/_v2warn when some or all sampled events predate that generation's real coverage start (GKG_V1_COVERAGE_START2013-04-01,GKG_V2_COVERAGE_START2015-02-18, both confirmed against GDELT's real file listings), the other way a join can legitimately find nothing besides a missing required column. Checked againstDATEADDED, notDay(which reflects when an event is reported to have occurred and can be far in the past for retrospective reporting). Diagnostic only: events within coverage in the same sample still join normallycrossref_events_gkg_auto(--gkg-version auto): attempts every eligible event against bothcrossref_events_gkg_v1andcrossref_events_gkg_v2, for a sample spanning both eras, e.g. the 2013-2015 window where only GKG 1.0 exists. Output concatenates both paths' results with aCrossrefSourcecolumn (v1/v2); an event matching both contributes one row per source. Events beforeGKG_V1_COVERAGE_STARTare skipped and logged, since neither generation has any data for them.--columnsisn't supported in this mode, since GKG 1.0's 11 fields and GKG 2.1's 27 share no common nameconvertgains a.donemarker for flat/daily output (Events daily archives, GKG 1.0, GKG 2.1, Mentions), matching the resumability the historical (Hive-partitioned) path already had and the skip-already-downloaded behaviorscrapealready had. Found necessary against a real 30,137-file Mentions batch: two independent runs each died to an OS-level kill around the same ~51% mark, having made no net progress relaunch to relaunch, because every attempt reprocessed every zip from file 1filtergains equivalent resumability, for the first time; its correctness surface is wider thanconvert's, sincecolumns_to_check,output_columns,float32_columns, andcompressionall change what the filtered output contains
Changed
filter.compressionnow defaults to zstd instead of snappy. Measured on real Events data (5.8M rows, all 58 columns): roughly 30% smaller than snappy, at comparable or faster write speed. Lossless, so no accuracy tradeoff in taking it as the default; per-dataset override remains available- README,
docs/index.md, anddocs/getting-started.mdnow lead withpip install gdeltforgeinstead of "install from a clone," now that 0.4.0 is actually on PyPI; the clone/uv syncpath is kept as a clearly-labeled "installing from source" option for contributors. Checked off the PyPI roadmap item, and fixed a leftover↔indocs/cli-reference.md(should have been<->, missed in the arrow-notation pass this session).pyproject.toml'sdescriptionwas also still the old Events-only tagline; fixed for the next release, though it won't retroactively change what's already published for 0.4.0
Fixed
_detect_file_typenever recognized real 15-minuteYYYYMMDDHHMMSSGKG 2.1/Mentions filenames as any known cadence, since the file-type patterns in place at the time GKG 2.1/Mentions support shipped all assumed 8-digit daily or longer. Nothing surfaced the gap in practice, only because partitioning is never enabled for those two datasets. Added a realquarter_hourlyfile type; the non-detecting partitioning-off shortcut, previously mislabeled"daily"regardless of a file's real cadence, is renamed"flat", and routing to the historical (Hive-partitioned) path is now decided by whetherpartitioning.rulesdefines an entry for the detected file type rather than by a hardcoded"daily"string comparison- The
.donemarkerconvert's historical (Hive-partitioned) path already had only ever recorded that a file had been processed, not under what configuration. Rerunningconvertafter changingoutput_columnswould be silently skipped by a marker left from the old configuration, serving output shaped by settings that no longer match the current run.config_fingerprint/is_marked_done/mark_donenow write the relevant config into the marker itself and compare its content, not just presence; a mismatch, including a pre-fingerprint empty marker from before this existed, is treated as not done crossref_events_gkg_v2hardcodedMentionTimeDateandConfidenceas required when reading Mentions, even though neither participates in the join itself (onlyGLOBALEVENTIDandMentionIdentifierdo). A Mentions dataset missing either one failed outright instead of joining successfully minus that payload field; both are now read only if presentcrossref_events_gkg_auto's routing excluded an event fromv2whenever its ownDATEADDEDpredatedGKG_V2_COVERAGE_START, on the reasoning that such an event could only match through GKG 1.0. Confirmed for real that reasoning doesn't hold: a Mentions row is timestamped by when it was created, not by its event'sDATEADDED(a 2019-origin event was found referenced by a real Mentions row dated 2020), and a direct test against the complete 2015-02-18 through 2015-12-31 Mentions history (211.8M rows) plus a further scattered sample found zero matches for any pre-2015-02-18 event, not because none could exist but because the old routing never gave them the chance. The same asymmetry ran the other direction too, since GKG 1.0 remains live and daily-published today. Every eligible event is now attempted against both generations rather than routed to exactly onecrossref_events_gkg_v2's dedup (drop_duplicates(keep="last"), keeping the most recently reprocessed article) silently depended onPath.glob's return order matching each file's real chronological position, true on NTFS by coincidence but not on Linux/ext4, where GitHub Actions CI failed by keeping the stale record instead of the reprocessed one._dataset()now sorts its file list explicitly