r2parquet 0.2.0
First release, moved from mountainmathHelpers.
- Reading:
parquet_tbl()gives lazy DuckDB tables on public URLs, presigned URLs and local files without credentials, and on private R2 buckets asr2://bucket/path. Directories at URLs, which can’t be listed over https, are read through the_manifest.jsonthat uploads write next to the data, so partitioned datasets on a public bucket work like local ones. The tables live on one connection shared for the session,parquet_connection(), so they can be joined with each other and with data copied there, or on a connection of your own passed ascon. Hive partition values are read as strings, whatever they look like, soPRUID=10andPRUID=01give the same column type, and numbers compared with a partition column for equality are rendered as strings, with a message once per session, sofilter(PRUID == 59)reads that partition alone instead of casting the column and reading all of them; order comparisons need the cast spelled out,as.integer(PRUID) > 10. Whole numbers in comparisons are rendered as integers, with a message once per session, sincefilter(year == 2021)would otherwise compare as doubles and read every row group of an integer column instead of the matching ones;2021Lavoids the message. The arrow and polars readers ofmountainmathHelperswere not carried over, neither handles GeoParquet 2.0 geometry, and uploads and listings use signed S3 requests instead of arrow, so the package has no optional dependencies. - Spatial queries:
sf::st_filter()works on any lazy DuckDB table and only downloads the row groups that intersect the query, with the sf predicates translated to DuckDB’s spatial functions and the query transformed to the crs of the data; DuckDB’s geometry is planar, so it warns for longitude/latitude data whilesf::sf_use_s2()is on,collect_parquet()collects as sf if the table has geometry. Collecting a remote table with every column it was read with gives a message, once per session, with theselect()of those columns to paste in and prune, since parquet only downloads the selected columns. - Writing:
data_to_parquet()writes data frames, sf objects and lazy DuckDB tables to parquet, optionally sorted (sort_by) and hive partitioned (partition_by). sf objects and tables with GEOMETRY columns become GeoParquet 2.0 files sorted along a Hilbert curve. Given anr2://bucket/pathdestination it writes and uploads in one go, removing earlier uploads at the same path so no stale partitions remain. It returns the path, ready forparquet_tbl(), with the files written in thefilesattribute.remove_stale = FALSEkeeps partitions of earlier writes, andcompressionpicks the codec, zstd by default. - Languages: datasets can carry labels and text columns in several languages. Labels and descriptions per language go into suffixed entries like
label_enandlabel_fr, with or without a plainlabel, a column holding another column’s text in a language is named with the same suffix,name_frnext tonameorname_enandname_fron their own, and thelanguagemetadata entry names the language of whatever plain labels and columns there are.parquet_tbl(language = "fr"), or ther2parquet.languageoption, selects the columns of that language in place of the plain ones on the lazy table, leaving the other languages’ columns out of the query, soname_fris queried asname;select_language()does the same on any lazy table fromparquet_tbl()or a join of such tables, e.g. after reading withlanguage = NULL, which gives the columns as stored.collect_parquet(language = "fr")attaches the labels of that language, falling back to the plain labels and then to the dataset language’s, labels a selected column with the label of the column it came from, describes a table whose columns were selected in a language in that language by default, and still swaps the columns at collect time where the table was read without a language. No language is privileged: a bilingual dataset can carry only suffixed labels and columns. The metadata attached to data collected in a language describes the result in that language:title_frandattribution_frtake the place oftitleandattribution, the other languages’ entries are left out and thelanguageentry names the language, so writing the result with its metadata gives a correct monolingual dataset;parquet_metadata(language =)returns the same without collecting and the metadata as stored by default,parquet_sources()andparquet_sources_attribution()take alanguagetoo (defaulting to the option, likecollect_parquet()). Metadata in several languages prints consolidated, one row per entry or column with a column per language. Collecting, or asking for the sources or attribution, without a language from a dataset that has text in several languages and no plain text gives a message, once per session, saying how to set one. - Metadata:
data_to_parquet(metadata = list(source = ..., license = ..., columns = ...))writes source, license, notes and column labels into the parquet footer,parquet_metadata()reads them back from paths, lazy tables or collected data, for paths on R2 and at URLs from the manifest next to the data (one request for a whole dataset) unlessfrom_files = TRUEasks for the footers themselves, andcollect_parquet()attaches them to the collected data as attributes, column labels aslabelattributes. - Local cache:
parquet_tbl(cache = TRUE)reads local copies of remote files for data that gets queried regularly, from the directory inR2PARQUET_CACHE_PATHor the user cache directory. Once cached,parquet_tbl()reads the copy by default as long as it is current, checking each copy once per session (clear_cache_checks()forgets the checks), andcache = "offline"reads it without checking.cache_parquet()does the syncing: it mirrorsr2://paths and URLs into the cache, compares etags so only new and changed files get downloaded, removes files that are gone remotely, checks downloads against their MD5 etag, and falls back to the cached copy with a warning when the remote can’t be reached.list_parquet_cache()lists the copies in the cache by the remote paths they stand for,remove_parquet_from_cache()removes them, andlist_parquet()reports in itscachecolumn whether a file has a current copy in the cache, a stale one or none. - Manifests:
upload_parquet()and partitioneddata_to_parquet()writes to R2 put a_manifest.jsonnext to the data listing the parquet files with size, etag and modification time, so that directories can be cached from public URLs, where listing is not possible, and the metadata from the files’ footers, shared entries at the dataset level and the rest per file, so clients can read descriptions and column labels without opening the files (the format is documented in?write_manifest).write_manifest()adds manifests to existing directories, reading the footers on R2, and rebuilds manifests that fell behind,remove_parquet()removes them with directories, and uploads and removals keep the manifests of the directories above (and, for directory uploads, below) the changed files current by patching them, so adding or removing a file or partition of a dataset updates the dataset’s manifest without listing it. Manifests are written conditionally, so concurrent uploads can’t overwrite each other’s updates, and a manifest that can’t be updated is reported with a warning once the data is up. Directories can’t be uploaded to the root of a bucket,write_manifest("r2://bucket")writes a manifest of the whole bucket on request. - Managing R2:
create_r2_bucket()creates private or public buckets,upload_parquet()andremove_parquet()upload and delete files and purge them from the Cloudflare cache of the bucket’s custom domains.upload_parquet(remove_stale = TRUE)removes the parquet files below the destination that are no longer in the uploaded directory, so a rebuilt dataset gets mirrored (off by default, unlike fordata_to_parquet(), which only removes its own partitions). Single files of any type that go with a dataset, like a JSON index, go up with the content type for their extension, andremove_parquet()removes them by path. File names with*,?or[are refused, DuckDB would read them as patterns.remove_parquet()takes several paths at once and resolves all of them before removing anything.list_parquet()lists the parquet files at R2 paths, URLs (files via their headers, directories via their manifest) or local paths, or the files a lazy table reads, with file name, size, modification time, cache status, etag and path. - Sharing private files:
presigned_url()creates time limited download (or upload) URLs for one or more files. - Files on R2 are given as
r2://bucket/paththroughout, for reading, writing, uploading, removing and presigning.
