-
Notifications
You must be signed in to change notification settings - Fork 51
feat: added the new datasets #1249
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
5dda44a
64b6379
499fbff
3ceeaa1
76c0ff4
9a8f552
418a8da
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,150 @@ | ||
| import os | ||
| import json | ||
| from typing import TYPE_CHECKING, Callable, Dict, List | ||
|
|
||
| import pandas as pd | ||
|
|
||
| from langtest.datahandler.utils import ensure_download_and_unzip | ||
|
|
||
| if TYPE_CHECKING: | ||
| from langtest.utils.custom_types.sample import Sample | ||
|
|
||
|
|
||
| PREDEFINED_DATASETS: Dict[str, Callable[..., List["Sample"]]] = {} | ||
|
|
||
|
|
||
| def register_predefined_dataset(name: str): | ||
| """Decorator to register a predefined dataset.""" | ||
|
|
||
| def decorator(func: Callable[..., List["Sample"]]): | ||
| PREDEFINED_DATASETS[name.lower()] = func | ||
| return func | ||
|
|
||
| return decorator | ||
|
|
||
|
|
||
| @register_predefined_dataset("medexqa") | ||
| def medexqa(subset="all", *args, **kwargs) -> List["Sample"]: | ||
| """Load the MedExQA dataset.""" | ||
| from langtest.utils.custom_types import QASample | ||
|
|
||
| # 1. Define the specific files and URL internally | ||
| file_names = [ | ||
| "biomedical_engineer", | ||
| "clinical_laboratory_scientist", | ||
| "clinical_psychologist", | ||
| "occupational_therapist", | ||
| "speech_pathologist", | ||
| ] | ||
| base_url = "https://huggingface.co/datasets/bluesky333/MedExQA/resolve/main/test/" | ||
|
|
||
| # 2. Filter the files based on the subset parameter | ||
| if subset != "all": | ||
| if subset not in file_names: | ||
| raise ValueError( | ||
| f"Subset '{subset}' is not valid. Choose from {file_names} or 'all'." | ||
| ) | ||
| file_names = [subset] | ||
| frames = [] | ||
|
|
||
| for file_name in file_names: | ||
| file_path = f"{base_url}{file_name}_test.tsv" | ||
|
|
||
| # 2. Read ONLY the required columns to save memory and parsing time | ||
| df = pd.read_csv( | ||
| file_path, delimiter="\t", header=None, usecols=[0, 1, 2, 3, 4, 7] | ||
| ) | ||
|
|
||
| # 3. Assign clear column names immediately | ||
| df.columns = ["question", "A", "B", "C", "D", "answer"] | ||
|
|
||
| # 4. Create the 'options' dictionary column | ||
| df["options"] = df[["A", "B", "C", "D"]].to_dict(orient="records") | ||
|
|
||
| # 5. Append only the necessary final columns to our list | ||
| frames.append(df[["question", "options", "answer"]]) | ||
|
|
||
| # 6. Concatenate all DataFrames at once | ||
| raw_data = pd.concat(frames, ignore_index=True).iterrows() | ||
| transformed_samples = [] | ||
|
|
||
| for sample in raw_data: | ||
| sample = QASample( | ||
| dataset_name="medexqa", | ||
| original_context="-", | ||
| original_question=sample[1]["question"], | ||
| options="\n".join([f"{k}. {v}" for k, v in sample[1]["options"].items()]), | ||
| expected_results=sample[1]["answer"], | ||
| ) | ||
|
|
||
| transformed_samples.append(sample) | ||
| return transformed_samples | ||
|
|
||
|
|
||
| @register_predefined_dataset("headqa") | ||
| def headqa(*args, **kwargs) -> List["Sample"]: | ||
| """Load the HeadQA dataset.""" | ||
| from langtest.utils.custom_types import QASample | ||
|
|
||
| headqa_dir = os.path.join(os.path.expanduser("~"), ".langtest", "datasets", "headqa") | ||
|
|
||
| ensure_download_and_unzip( | ||
| "https://huggingface.co/datasets/dvilares/head_qa/resolve/main/data/head-qa-es-en-pdfs.zip", | ||
| extract_to=headqa_dir, | ||
| ) | ||
|
|
||
| file_path = os.path.join(headqa_dir, "HEAD_EN", "test_HEAD_EN.json") | ||
|
|
||
| with open( | ||
| file_path, | ||
| "r", | ||
| encoding="utf-8", | ||
| ) as f: | ||
| head_qa = json.load(f) | ||
|
|
||
| def clean_answers(answers): | ||
| return "\n".join( | ||
| f"{chr(answer['aid'] + 64)}) {answer['atext'].strip()}" for answer in answers | ||
| ) | ||
|
|
||
| df = ( | ||
| pd.DataFrame.from_dict(head_qa["exams"], orient="index") | ||
| .reset_index(drop=True) | ||
| .assign( | ||
| exam_id=lambda x: x.index, | ||
| name=lambda x: x["name"].str.strip(), | ||
| year=lambda x: x["year"].str.strip(), | ||
| category=lambda x: x["category"].str.strip(), | ||
| ) | ||
| .pipe( | ||
| lambda x: pd.json_normalize( | ||
| x.to_dict("records"), | ||
| record_path="data", | ||
| meta=["exam_id", "name", "year", "category"], | ||
| ) | ||
| ) | ||
| .assign( | ||
| qid=lambda x: x["qid"].str.strip().astype(int), | ||
| qtext=lambda x: x["qtext"].str.strip(), | ||
| ra=lambda x: x["ra"].str.strip().astype(int), | ||
| options=lambda x: x["answers"].apply(clean_answers), | ||
| ) | ||
| .query("ra != 0") | ||
| .assign( | ||
| answer=lambda x: x["ra"].map(lambda value: chr(value + 64)), | ||
| )[["qid", "qtext", "options", "answer"]] | ||
| ) | ||
|
|
||
| transformed_samples = [] | ||
|
|
||
| for sample in df.iterrows(): | ||
| sample = QASample( | ||
| dataset_name="headqa", | ||
| original_context="-", | ||
| original_question=sample[1]["qtext"], | ||
| options=sample[1]["options"], | ||
| expected_results=sample[1]["answer"], | ||
| ) | ||
|
|
||
| transformed_samples.append(sample) | ||
| return transformed_samples |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -114,3 +114,47 @@ def process_document(doc): | |
| } | ||
|
|
||
| return json_output | ||
|
|
||
|
|
||
| def ensure_download_and_unzip(url: str, extract_to: str): | ||
| """ | ||
| Ensures that a file is downloaded from the given URL | ||
| and unzipped to the specified directory. | ||
|
|
||
| Args: | ||
| url (str): The URL of the file to download. | ||
| extract_to (str): The directory where the file should be extracted. | ||
|
|
||
| This function checks if the specified directory exists. If it does not exist, | ||
| it creates the directory, downloads the file from the given URL, and extracts its contents into the directory. | ||
|
|
||
|
|
||
| """ | ||
| import requests | ||
| import zipfile | ||
| import io | ||
| import os | ||
|
|
||
| try: | ||
| # 1. Critical Check: Exit early if the path already exists | ||
| if os.path.exists(extract_to): | ||
| print(f"Skipping download. Path '{extract_to}' already exists.") | ||
|
|
||
| else: | ||
| # 2. Download the file (Removed stream=True since response.content reads all at once) | ||
| response = requests.get(url) | ||
| response.raise_for_status() | ||
|
|
||
| # 3. Create the folder structure | ||
| os.makedirs(extract_to, exist_ok=True) | ||
|
|
||
| # 4. Unzip directly from memory | ||
| with zipfile.ZipFile(io.BytesIO(response.content)) as zip_ref: | ||
| zip_ref.extractall(extract_to) | ||
|
|
||
| print(f"Successfully downloaded and extracted to {extract_to}") | ||
|
|
||
| except requests.exceptions.RequestException as e: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix ensure_download_and_unzip error handling -> Exceptions are caught and only printed; the function returns None and callers proceed as if the download succeeded. If the directory exists but is incomplete or corrupted from a previous interrupted run, the code silently reuses it. |
||
| print(f"Error downloading {url}: {e}") | ||
| except zipfile.BadZipFile: | ||
| print("Error: The downloaded file is not a valid ZIP file.") | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add timeouts, retries, and validation to the network download