Spaces:
Running
Running
File size: 8,001 Bytes
3a1d713 88b1ea9 3a1d713 5411f5f bb4c28d 88b1ea9 726d852 0b5c981 88b1ea9 3a1d713 88b1ea9 bb4c28d 3a1d713 88b1ea9 726d852 bb4c28d 88b1ea9 3a1d713 88b1ea9 0b5c981 88b1ea9 5411f5f bb4c28d 726d852 3a1d713 88b1ea9 0b5c981 88b1ea9 5411f5f 726d852 88b1ea9 3a1d713 bb4c28d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | """Bundled example corpora, so a visitor can run CCR without their own data.
Every entry is a file in sample_data/ plus the provenance a researcher needs to
cite it. The citation travels into run metadata (see jobs.py), because results
produced on someone else's corpus have to carry the attribution into whatever
the researcher publishes - a demo dataset is still data with authors.
Adding one: drop the CSV in sample_data/, add an entry here, and say in
`preprocessing` exactly what was done to it. A test checks every file exists,
parses, and has the declared text column.
"""
from __future__ import annotations
from dataclasses import asdict, dataclass
from pathlib import Path
SAMPLES_DIR = Path(__file__).resolve().parents[2] / "sample_data"
@dataclass(frozen=True)
class ExampleCorpus:
"""A corpus offered in Step 1.
Small ones ship in sample_data/ (`filename`); large ones live in object
storage under the `examples/` prefix (`storage_key`), because a 38 MB file
in the repo would ride in the Docker image and the Space repo on every
deploy. Exactly one of the two is set.
"""
id: str
name: str
filename: str
text_column: str
language: str
n_rows: int
description: str
citation: str
source_url: str
preprocessing: str
# The corpus's OWN licence, which is not the platform's. A researcher has to
# know the terms they are working under before they analyse someone else's
# data, so it is shown wherever the corpus is described.
license: str = ""
# First in the list and selected on load. NOT a claim that it is 'the'
# corpus: the PI's ask was for CAMEL to be available and ready to use.
preselected: bool = False
storage_key: str = ""
# Optional mark for the dataset, served from sample_data/ at /samples.
# Shown in the info dialog, where it identifies THIS dataset - not in the
# picker row, which lists several and would imply they share a source.
logo: str = ""
# One line under the name in the sample-data list, so a researcher can judge
# fit before running. The row already shows the count and language.
detail: str = ""
@property
def path(self) -> Path:
return SAMPLES_DIR / self.filename
@property
def bundled(self) -> bool:
return not self.storage_key
def available(self) -> bool:
"""Bundled files are checked on disk; stored ones are assumed present -
a HEAD on every listing would add a network round trip to page load,
and a missing object surfaces as a clear 404 on selection instead."""
return self.path.exists() if self.bundled else True
def blocked_reason(self, row_ceiling: int) -> str:
"""Why this instance cannot run this corpus, or "" if it can.
A corpus larger than CCR_MAX_ROWS is refused at ingest, so offering it
as a working option produces a dead end. Saying so up front, and why,
beats an error after the click - and it corrects itself the moment the
deployment raises the limit.
"""
if not self.available():
return "Not loaded on this instance."
if row_ceiling and self.n_rows > row_ceiling:
return (
f"Too large for this instance: {self.n_rows:,} texts against a "
f"{row_ceiling:,}-row limit."
)
return ""
def public(self, row_ceiling: int = 0) -> dict:
data = asdict(self)
data["bundled"] = self.bundled
data["logo_url"] = (
f"/samples/{self.logo}" if self.logo and (SAMPLES_DIR / self.logo).exists() else ""
)
data.pop("logo", None)
blocked = self.blocked_reason(row_ceiling)
data["usable"] = not blocked
data["blocked_reason"] = blocked
data.pop("storage_key", None) # internal location, not the user's business
return data
EXAMPLES: list[ExampleCorpus] = [
ExampleCorpus(
id="camel_sample",
name="CAMEL corpus (sample)",
filename="camel_sample.csv",
text_column="text",
language="en",
n_rows=999,
description=(
"The Cultural and Moral Expressions in Language (CAMEL) corpus consists of "
"over 57 thousand texts taken from several platforms and annotated for 25 "
"different cultural and moral constructs. This is a 999-text sample drawn "
"from it, keeping the platform mix of the full corpus (Reddit, Twitter, "
"IMDB, news, Wikipedia, and others). The full corpus, including the "
"annotations, is on Hugging Face."
),
citation=(
"Zewail, A., Setia, A., Mohammadsadegh, R., Seker, F., Hajian, A., Sosa, H., "
"Morhayim, L., Reddy, S., & Atari, M. (2026). The Cultural and Moral "
"Expressions in Language (CAMEL) corpus [Preprint]."
),
source_url="https://huggingface.co/datasets/Culture-and-Morality-Lab/CAMEL_Dataset",
preprocessing=(
"Usernames were removed by the corpus authors. For this sample: texts drawn "
"proportionally by source platform with a fixed seed "
"(scripts/build_camel_sample.py), whitespace trimmed, and only the text and "
"source_platform columns kept. Text is otherwise verbatim."
),
detail=(
"Median 25 words, mean 69. Runs in seconds on any model. "
"Best for a first look at how CCR works."
),
license="CC BY-NC 4.0 (Creative Commons Attribution-NonCommercial 4.0). Free for non-commercial research with attribution; see the dataset page for the full terms.",
preselected=True,
logo="camel_logo.png",
),
ExampleCorpus(
id="camel_full",
name="CAMEL corpus (full)",
filename="",
storage_key="examples/camel_full.csv",
text_column="text",
language="en",
n_rows=57174,
description=(
"The complete Cultural and Moral Expressions in Language (CAMEL) corpus: "
"57,174 texts from Reddit, Twitter, IMDB, news, Wikipedia, the Internet "
"Archive and other sources, annotated by the lab for 25 cultural and moral "
"constructs. The annotation columns come with it, so CCR scores can be "
"compared against the human ratings."
),
citation=(
"Zewail, A., Setia, A., Mohammadsadegh, R., Seker, F., Hajian, A., Sosa, H., "
"Morhayim, L., Reddy, S., & Atari, M. (2026). The Cultural and Moral "
"Expressions in Language (CAMEL) corpus [Preprint]."
),
source_url="https://huggingface.co/datasets/Culture-and-Morality-Lab/CAMEL_Dataset",
preprocessing=(
"Usernames were removed by the corpus authors. Otherwise the corpus exactly "
"as published: all 64 columns, text verbatim, no sampling."
),
detail=(
"Median 26 words, up to 1,770. Expect a few minutes on MiniLM and "
"considerably longer on the large models; pick MiniLM unless you have a "
"reason not to."
),
license="CC BY-NC 4.0 (Creative Commons Attribution-NonCommercial 4.0). Free for non-commercial research with attribution; see the dataset page for the full terms.",
logo="camel_logo.png",
),
]
BY_ID = {e.id: e for e in EXAMPLES}
def get(example_id: str) -> ExampleCorpus | None:
return BY_ID.get(example_id)
def listed(row_ceiling: int = 0) -> list[dict]:
"""Examples this instance has, preselected one first.
Entries too large for the row ceiling are still listed but marked unusable
with a reason, rather than hidden: a corpus quietly vanishing is harder to
explain than one that says why it is unavailable.
"""
return [
e.public(row_ceiling)
for e in sorted(EXAMPLES, key=lambda e: not e.preselected)
if e.available()
]
|