6  Metadata from data

library(croissant)

croissant_from_data() writes an R table to a data file and returns a Croissant object that describes the file.

croissant_from_data(
  x,
  file,
  name,
  format = c("csv", "parquet"),
  record_set = "default",
  content_url = basename(file),
  version = "1.1",
  ...
)
Argument Meaning
x A data.frame, data.table, or tibble
file Path of the data file to write
name Name of the dataset
format "csv", written with data.table::fwrite(), or "parquet", written with arrow::write_parquet()
record_set @id and name of the record set; must contain no whitespace
content_url contentUrl of the file, relative to the metadata document
version Croissant version, "1.0" or "1.1"
... Further properties of the Croissant object

6.1 Generated metadata

The returned object contains:

  • one FileObject, whose @id and name are the file’s base name, with contentUrl set to content_url, contentSize to the file size in bytes ("<bytes> B"), encodingFormat to text/csv or application/x-parquet, and sha256 to the file’s SHA-256 checksum;
  • one RecordSet, whose @id and name are record_set;
  • one Field per column, with @id <record_set>/<column>, name equal to the column name, a source that extracts the column by name, and a data type determined by the column class.
Column class Data type
integer, integer64 sc:Integer
double sc:Float
logical sc:Boolean
Date sc:Date
POSIXct sc:DateTime
character, factor sc:Text

A column of any other class raises an error of class rtemis_type_error. In field @ids, whitespace in column names is replaced by _, and duplicate @ids receive make.unique() suffixes. The default record set name, "default", is the name that the Hugging Face Hub uses for a dataset’s single record set.

6.2 Example

dir <- file.path(tempdir(), "iris")
dir.create(dir, showWarnings = FALSE)

x <- croissant_from_data(
  iris,
  file = file.path(dir, "iris.csv"),
  name = "iris",
  description = "Measurements of 150 iris flowers from three species.",
  license = "CC0-1.0",
  cite_as = "Fisher RA. The use of multiple measurements in taxonomic problems. Annals of Eugenics. 1936;7(2):179-188.",
  creator = list(Person(name = "R. A. Fisher")),
  date_published = "1936",
  dataset_version = "1.0.0",
  keywords = c("classification", "botany")
)
x
<Croissant>
  name: iris
  version: Croissant 1.1
  distribution: 1 FileObject
  record sets: 1
    default: 5 fields
x@record_set[[1]]@field[[5]]@data_type
[1] "sc:Text"
validate_croissant(x)
<CroissantIssues>
  0 errors, 0 warnings

... accepts any Croissant property, including the Responsible AI properties, such as data_collection and data_limitations.

6.3 Writing and reading back

Write the metadata into the directory of the data file, where the relative contentUrl resolves:

metadata <- file.path(dir, "croissant.json")
write_croissant(x, metadata)
iris2 <- read_record_set(metadata, "default", output = "data.frame")
head(iris2)
  default/Sepal.Length default/Sepal.Width default/Petal.Length
1                  5.1                 3.5                  1.4
2                  4.9                 3.0                  1.4
3                  4.7                 3.2                  1.3
4                  4.6                 3.1                  1.5
5                  5.0                 3.6                  1.4
6                  5.4                 3.9                  1.7
  default/Petal.Width default/Species
1                 0.2          setosa
2                 0.2          setosa
3                 0.2          setosa
4                 0.2          setosa
5                 0.2          setosa
6                 0.4          setosa

The records read back equal the input table, with the field @ids as column names and factors as character columns:

all.equal(
  setNames(iris2, names(iris)),
  transform(iris, Species = as.character(Species))
)
[1] TRUE

6.4 CSV and Parquet

Both formats write a factor as its labels, which read back as character. CSV writes a missing value as an empty field and an empty string as "", and reads them back as NA and "". Parquet stores the column types directly.

df <- data.frame(
  id = 1:3,
  label = c("a", "", NA),
  when = as.POSIXct(c("2026-01-01 08:00:00", "2026-01-02 09:30:00", NA), tz = "UTC")
)

csv <- croissant_from_data(df, file.path(dir, "df.csv"), name = "df")
write_croissant(csv, file.path(dir, "df-csv.json"))
read_record_set(file.path(dir, "df-csv.json"), "default")
   default/id default/label        default/when
        <int>        <char>              <POSc>
1:          1             a 2026-01-01 08:00:00
2:          2               2026-01-02 09:30:00
3:          3          <NA>                <NA>
pq <- croissant_from_data(df, file.path(dir, "df.parquet"), name = "df", format = "parquet")
write_croissant(pq, file.path(dir, "df-parquet.json"))
read_record_set(file.path(dir, "df-parquet.json"), "default")
   default/id default/label        default/when
        <int>        <char>              <POSc>
1:          1             a 2026-01-01 08:00:00
2:          2               2026-01-02 09:30:00
3:          3          <NA>                <NA>

The package test suite checks that the output of croissant_from_data(), for a table with integer, double, logical, character, factor, Date, POSIXct, and whitespace-named columns, in CSV and Parquet and in Croissant 1.0 and 1.1, validates without issue and reads back to the input table. On 2026-10-27, mlcroissant 1.1.1 reported no error or warning on the same four documents and loaded the same values.

© 2026 E.D. Gennatas