ECHO Data Package 1.0.0

A common layout for every dataset ECHO distributes: the original files, unchanged, plus the same data converted to a small set of standard tables with documented columns, in open formats, with machine-readable metadata, licence and credit.

This page describes version 1.0.0 of the standard (draft).

Standards used

File Standard Role
datapackage.json Frictionless Data Package 2.0 Package descriptor: resources, Table Schema for every table, licences, sources and contributors.
croissant.json MLCommons Croissant 1.1 Machine-learning metadata read by dataset search engines and ML platforms; generated from datapackage.json.
CITATION.cff Citation File Format 1.2.0 Citation metadata; BibTeX in CITATION.bib carries the same entries.
LICENSE.md SPDX License List Licences are SPDX identifiers or expressions; terms with no SPDX identifier use a LicenseRef- with the full text.
manifest-sha256.txt BagIt manifest RFC 8493 A SHA-256 checksum for every file in the package.
data/*.parquet Apache Parquet The canonical tables: typed, compressed (zstd), columnar, and queryable over HTTP.
csv/*.csv.gz CSV RFC 4180 Copies of the tables for any tool: UTF-8, header row, gzip.
jsonl/*.jsonl.gz JSON Lines Copies of text and nested tables, one record per line, gzip.
graph/graph.graphml.gz GraphML Networks in a format graph tools open directly.

Layout

A package lives at /download/<slug>/. /download// redirects to the latest version folder. Each version has its own folder, /download/<slug>/v<MAJOR.MINOR.PATCH>/, containing:

Path Required Contents
README.md yes The human entry point, written by ECHO for this dataset.
datapackage.json yes Frictionless descriptor: every table and column, licence, sources, contributors.
croissant.json yes Croissant metadata for ML tools, generated from datapackage.json.
CITATION.cff yes How to cite: ECHO's entry first, then the dataset's.
CITATION.bib yes The same citations in BibTeX.
LICENSE.md yes The SPDX licence, the upstream licence text verbatim, and ECHO's redistribution note.
CHANGELOG.md yes What changed in each version, and why.
manifest-sha256.txt yes SHA-256 of every file in the version folder.
data/ yes The canonical tables in Parquet. This is the contract; every other format is generated from it.
csv/ where applicable The tables as gzip CSV, where policy.yaml gives a table a CSV copy.
jsonl/ where applicable Text and nested tables as gzip JSON Lines, where policy.yaml gives a table a JSONL copy.
graph/ where applicable Networks only: edges.tsv.gz and nodes.tsv.gz, plus GraphML where policy.yaml allows.
docs/upstream/ where applicable The upstream README, codebook and licence files, verbatim, under their original names.
original/ where applicable The upstream files exactly as published, where the licence allows redistribution.

Rules:

  • A published version folder never changes. Corrections are a new version with a CHANGELOG entry. One exception: while ECHO's datasets are first being converted to this format, every package is released as 1.0.0, and a correction during that period rebuilds 1.0.0 in its place (its manifest-sha256.txt changes with it). Once the conversion is complete, this exception ends.
  • data/ is the contract. csv/, jsonl/ and graph/ are generated from it and never edited by hand.
  • Every table can be downloaded, or queried over HTTP, on its own.
  • original/ holds the upstream files byte for byte; nothing in it is ever modified.
  • Operating-system junk (__MACOSX, .DS_Store, ._*, Zone.Identifier, Thumbs.db, .ipynb_checkpoints) and saved error pages are never packaged.
  • Every package records the recipe that built it: datapackage.json gives the recipe's commit in ECHO's recipe repository and the SHA-256 of its text, so a package can be rebuilt and checked.
  • A partitioned table's parts may be the same files as the tables of other packages: a year of a monthly series, for example, is made of the months' files. They carry the same checksums; nothing is copied or changed.
  • Licence: the package carries the licence the original publisher states. Where the publisher states none, the licence is NOASSERTION and LICENSE.md says so: ECHO assigns no licence and redistributes the publicly released files for research and teaching, with credit. Terms that are not a standard licence (for example research use only) are quoted verbatim under a LicenseRef identifier. A licence that a re-uploader declared on content they did not create is reported in the note but not applied. A lossless change of format is a technical modification, not an adaptation, so NoDerivatives licences are respected.

Conventions

Names. Tables and columns are snake_case. A platform-specific table or column keeps its meaning under an x_ prefix (x_score, x_gilded, x_votes), so it can never collide with a standard one. x_ stands for extension; it has nothing to do with the platform X (Twitter).

Identifiers.

  • Every ID is a string, exactly as upstream wrote it. IDs are never converted to numbers, because large integers lose digits as floating-point numbers.
  • Where upstream gives no ID, ECHO mints one as echo::; the column's schema marks it minted, and minted IDs are stable only within one version.
  • Where two kinds of node share a numbering (bipartite and heterogeneous graphs), node IDs are prefixed with their type: user:17, item:17.
  • IDs that were damaged upstream (rounded by a spreadsheet, written in scientific notation) are kept as found and flagged in the column's quality note. They are never repaired, because a repaired ID would point to a different record.

Time.

  • Absolute times are ISO 8601 timestamps in UTC (created_at), kept to the microsecond. The upstream time zone or offset, where known, is recorded in the column's schema; a source time finer than a microsecond is kept as a string.
  • Where upstream gives no time zone and the data give no evidence of one, the time is kept as text exactly as written, not presented as UTC; the column note says so.
  • Where upstream recorded only a date, the column is a date, not a timestamp at midnight.
  • Relative or ordinal times (seconds since the start of a study, "26 days ago", survey waves) go in a column t with a declared time_unit and time_origin. They are never presented as dates.

Text.

  • Text is UTF-8, kept exactly as the source wrote it: no Unicode normalisation, lower-casing, stripping, HTML unescaping or spelling correction. Text from different sources may therefore use different Unicode forms for the same characters; normalise (for example to NFC) before comparing or matching text.
  • Where upstream text is mis-encoded (mojibake), it is kept as found and the column carries a quality note; the original bytes remain in original/.
  • Where upstream text is not valid UTF-8, the column holds the text decoded with replacement characters and an x__bytes column holds the original bytes (as hexadecimal text) for exactly those rows, so nothing is lost.
  • Where upstream text is not valid UTF-8 and the package also holds a clean copy of the same records (for example a Kaggle copy of a CrowdFlower file), the text column holds the clean copy's text for every record that could be matched to exactly one record of the clean copy on fields other than the text itself. The upstream text is kept as found in x_ for every row, with its original bytes in x_bytes, and x_source names the file each value of the text column came from. The README notes give the matching fields and say how many rows take their text from which file; a record that cannot be matched without doubt keeps the upstream text.

Values.

  • Booleans are true booleans, never the strings "True"/"False" or 0/1 codes.
  • Missing values are null. Upstream sentinels (-1, NaN, "", "N/A") become null only where upstream documents them as missing; otherwise they are kept and noted.
  • Lists, vectors and nested records are JSON text columns (type json) in EDP 1.0; their structure is described in the column note.
  • Numbers keep their upstream precision; counts are integers.
  • Placeholder values: a value that stands in for a missing, hidden or anonymous entity rather than naming a real one (an author written as None, as 1234567? or as [deleted]) is kept exactly as found, never turned into null, because upstream does not document it as missing. The recipe declares such values for a string column, and the builder counts them in the built table and writes them to the column's field metadata in datapackage.json, as echo:placeholders: a list of entries {value or pattern, count, distinct, about}, where value is an exact string, pattern is a regular expression (RE2 syntax) that must match the whole value, count is the number of rows of this package that hold such a value, distinct the number of different values among them, and about says what the value stands for. A consumer that wants real entities only (users, authors) excludes these values; a column without echo:placeholders declares none.

Lossless conversion.

  • Every conversion is checked against the source: row counts per table, and a value-by-value comparison of a sample of rows. A conversion that loses or changes information fails the build.
  • Where a source value cannot be represented in its standard column without loss, it is kept as a string in an x_ column and noted.

Standard tables

A package contains the tables its data supports. Columns marked required are present in every copy of that table; the others appear where the data has them. A column specific to one platform keeps its meaning under an x_ prefix. Types: string, integer, number, boolean, timestamp (UTC), date, list and json.

posts

Pieces of content people wrote: posts, comments, replies, tweets, messages, reviews.

Primary key: post_id.

Column Type Required Meaning
post_id string yes The post's ID as upstream wrote it.
author_id string Who wrote it; joins to users.user_id.
created_at timestamp When it was posted (UTC).
t number Relative time, where the source has no absolute time; see time_unit and time_origin.
title string
text string The content, as written. Null where the package does not include text.
parent_id string The post this one replies to.
thread_id string The conversation it belongs to; joins to threads.thread_id.
community_id string Where it was posted (subreddit, board, group, channel); joins to communities.community_id.
lang string Language, as a BCP 47 tag, where upstream recorded it.
url string
item_id string The item it is about (video, product, article); joins to items.item_id.
snapshot_id string Which crawl or snapshot it comes from, for repeated collections.
collection_member string Which member dataset it belongs to, in a collection package.

threads

Conversations or discussion threads.

Primary key: thread_id.

Column Type Required Meaning
thread_id string yes
community_id string
title string
created_at timestamp
root_post_id string

users

Accounts, users or authors.

Primary key: user_id.

Column Type Required Meaning
user_id string yes
handle string Screen name, where the licence and privacy review allow it.
name string Display name, where the licence and privacy review allow it.
created_at timestamp
is_bot boolean Only where upstream labelled it.

communities

Places where content is posted: subreddits, boards, groups, channels, instances.

Primary key: community_id.

Column Type Required Meaning
community_id string yes
name string
platform string
description string

memberships

Who belongs to which group.

Column Type Required Meaning
user_id string yes
group_id string yes
role string
joined_at timestamp

nodes

Nodes of a network, with their attributes.

Primary key: node_id.

Column Type Required Meaning
node_id string yes
node_type string For networks with more than one kind of node.
label string
layer string For packages with several networks.
snapshot_id string For attributes recorded per crawl: the snapshot this row describes.

edges

Links of a network. One row per link as upstream recorded it: repeated links and events are never merged.

Column Type Required Meaning
src string yes
dst string yes
weight number
sign integer +1 or -1 in signed networks.
created_at timestamp
t number Relative time; see time_unit and time_origin.
edge_type string
layer string Which network the link belongs to, in multilayer packages.
snapshot_id string
rank integer

interactions

A user acting on an item: ratings, likes, check-ins, plays, bookmarks.

Column Type Required Meaning
user_id string yes
item_id string yes
kind string
value number The rating or count, where there is one.
created_at timestamp

items

Things users act on or post about: videos, products, images, articles, venues.

Primary key: item_id.

Column Type Required Meaning
item_id string yes
kind string
title string
url string
snapshot_id string For records taken from repeated crawls: the snapshot this row describes.

annotations

Labels attached to posts, users, threads, links or text spans, by people, models or rules.

Column Type Required Meaning
target_id string yes
target_type string yes post
task string What is being labelled (toxicity, stance, check-worthiness…).
label string The label as upstream coded it.
label_text string What the code means, from upstream documentation.
span_start integer
span_end integer
annotator string
annotator_kind string human
confidence number
split string train

media

Images, video or audio attached to posts.

Primary key: media_id.

Column Type Required Meaning
media_id string yes
post_id string
kind string
path string Path of the file inside original/.
url string
mime string

ids

Identifiers only, for collections whose terms allow sharing IDs but not content.

Column Type Required Meaning
id string yes
id_type string yes tweet
collection string
collected_at timestamp

snapshots

The crawls or snapshots in a repeated collection.

Primary key: snapshot_id.

Column Type Required Meaning
snapshot_id string yes
taken_at timestamp
description string

splits

Alternative or k-fold splits.

Column Type Required Meaning
target_id string yes
split string yes
fold integer
scheme string

timeseries

Aggregate measurements over time, in long format.

Column Type Required Meaning
entity_id string yes
period string yes ISO 8601 date or period.
metric string yes
value number

features

Precomputed features or embeddings, with a feature dictionary in the schema.

Column Type Required Meaning
target_id string yes
vector list

Structure properties

Each package is tagged with the structural properties of its data. They are computed from the tables themselves, and can be used to find datasets on ECHO (Browse → Structure).

Property Meaning
Directed network Edges declared directed
Weighted network Edges.weight present and not constant
Signed network Edges.sign present
Bipartite network Two node types, every edge joins different types
Multigraph (repeated links) Some (src, dst[, layer]) pair occurs more than once
Temporal (timestamped) Edges or posts carry created_at or t
Repeated crawls / snapshots Snapshots table has 2 or more rows
Multilayer (several networks) Edges.layer has 2 or more values
Node attributes Nodes or users have columns beyond the ID
Labelled data Annotations table present
Text Posts.text present
Threaded conversations Posts.parent_id or threads present
Images or media Media table present
Identifiers only Ids table present and no content tables
Collection Package has member views

Collections, views and derived packages

  • view of: This package is a filtered view of a collection package: {package, version, filter}.
  • derived from: This package is a slice or an enrichment of another package: {package, version, how}.
  • members: This package is a collection; its member packages are listed with their filters.

Metadata

Contributors. Everyone credited, with their role: the creators of the data, the original publisher, every redistributor in between (for example SNAP → KONECT → Network Repository), and ECHO as packager. Taken from upstream documentation, never guessed. Roles: creator, contributor, publisher, distributor, packager.

Privacy review. What personal information the package contains, what was withheld and why. A withheld column stays in the schema, marked withheld, so users can see that it exists.

Quality notes. Per-column notes on known upstream problems (damaged IDs, mis-encoded text, sentinel values), so no user mistakes them for ECHO's conversion.

Fields for software and AI tools. Fields that let APIs, ML pipelines and language-model tools use a package without reading its README.

  • text_columns: The columns holding natural-language text, with their language where known.
  • id_columns: The columns holding identifiers, and what they join to.
  • row_license: Where the licence varies by row, the column that holds it.
  • embedding_columns: Vector columns, with the model that produced them.
  • languages: The package-level languages list in datapackage.json: the languages of the dataset's text as recorded in ECHO's catalog when the package metadata was written, as lower-case ISO 639 codes such as en, de or fil, sorted. It describes the dataset as a whole, not individual columns or rows, and is absent where the catalog records no language. Packages published before this property was added do not carry it; the catalog is the source for them.
  • placeholders: The echo:placeholders list of a column's schema field: placeholder values (see Values above) with their counts, so that a tool can tell them from real entities without reading the README.

The README

Every package's README.md has these sections, in this order:

  1. </strong>: One paragraph describing the dataset, written for it.</li> <li><strong>At a glance</strong>: Platform, kind of data, period covered, size, version, licence, DOI.</li> <li><strong>Contents</strong>: Every file, its role and format.</li> <li><strong>Tables and columns</strong>: Generated from datapackage.json: every column with type, meaning and quality notes.</li> <li><strong>Quick start</strong>: Loading the main table in Python (pandas), DuckDB and R.</li> <li><strong>How this package was made</strong>: The source files, what was converted, what was left out and why, known issues.</li> <li><strong>Credit</strong>: Creators, publisher and redistributors, with links to the upstream documentation.</li> <li><strong>Licence and terms</strong>: The licence and what it permits.</li> <li><strong>Citation</strong>: ECHO's entry first, then the dataset's.</li> <li><strong>Versions</strong>: This version, the changelog, and how to cite a specific version.</li> </ol> <h2 id="versions">Versions</h2> <p>Versions are numbered MAJOR.MINOR.PATCH.</p> <ul> <li><strong>MAJOR</strong>: A table or column is removed or changes meaning, or rows are removed.</li> <li><strong>MINOR</strong>: Rows, tables or columns are added, or upstream published new data.</li> <li><strong>PATCH</strong>: Documentation or metadata only; the data is unchanged.</li> </ul> <p>datapackage.json records the recipe revision and the SHA-256 of every original file used, so any version can be rebuilt exactly.</p> <h2 id="checks-before-publication">Checks before publication</h2> <p>A package version is published only when all of these checks pass:</p> <ol> <li>datapackage.json is valid; every table matches its Table Schema, primary keys are unique and declared foreign keys resolve. Where a table has snapshot_id or layer columns, the key is unique within each snapshot and layer.</li> <li>Each table's row count matches the source and the previous version, within the tolerance in policy.yaml; a larger change needs an admin's approval.</li> <li>A sample of rows is compared value by value with the source files.</li> <li>No original is an HTML or error page, zero bytes, operating-system junk, or a file whose content contradicts its extension; no pickle reaches data/.</li> <li>Text columns are valid UTF-8 with no NUL bytes; mojibake and ID columns in scientific notation are flagged.</li> <li>Text and user columns are scanned for e-mail addresses, phone numbers and street addresses; findings go to the privacy review (they do not block on their own).</li> <li>An SPDX licence expression is present, with the upstream text for any LicenseRef.</li> <li>An original already published in another package is declared as a view, a derivative or a shared original.</li> </ol> </article> </div> </main> <footer class="site-footer"> <div class="wrap cols"> <div> <h4>ECHO</h4> <p class="small">ECHO: Exploratory Compendium of Humanity Online<br>Braden Switzer & Reza Zafarani — Syracuse University</p> <p class="footer-lab"><a class="lab-logo" href="https://data.syr.edu/" title="Data Lab, Syracuse University"><img src="/assets/datalab.png?v=70bb63ceab1f" alt="Data Lab" width="160" height="38"></a></p> <p class="small subtle">1127 datasets indexed. Last built 2026-11-09.</p> </div> <div> <h4>Browse</h4> <ul class="small"> <li><a href="https://echo.directory/browse/">All datasets</a></li> <li><a href="https://echo.directory/platform/">Platforms</a></li> <li><a href="https://echo.directory/topic/">Topics</a></li> <li><a href="https://echo.directory/stats/">Statistics</a></li> <li><a href="https://echo.directory/new/">Recently added</a></li> </ul> </div> <div> <h4>Use</h4> <ul class="small"> <li><a href="https://echo.directory/about/">About</a></li> <li><a href="https://echo.directory/how-to-cite/">How to cite</a></li> <li><a href="https://echo.directory/terms/">Terms & licensing</a></li> <li><a href="mailto:echo@data.syr.edu">Contact</a></li> </ul> </div> </div> </footer> </body> </html>